Security Vulnerabilities by Category 2026
TL;DR
Input parsing, HTTP handling, and cryptography packages have the highest vulnerability density in the npm ecosystem. But frequency of CVEs isn't the same as severity — many high-CVE categories get patched quickly, while some rarely-updated categories silently accumulate vulnerabilities. The worst combination: old input-parsing packages that nobody has audited in years, sitting in your dependency tree processing untrusted user data.
Key Takeaways
- Input parsing (XML, CSV, YAML, URL) — highest CVE density, especially ReDoS and prototype pollution
- Authentication packages — critical severity when vulnerable (credentials, tokens)
- Cryptography wrappers — silent failure modes are the most dangerous
- Build tools — mostly dev vulnerabilities (less critical, still patch them)
- The real risk: time between CVE disclosure and your patch adoption
Vulnerability Distribution by Category (npm, 2024-2026)
CVE count by package category (approximate, based on npm advisories):
Input/Parsing: 38% of CVEs
├── Regular expressions (ReDoS): 45% of parsing CVEs
├── Prototype pollution: 30%
├── Buffer overflow: 15%
└── Path traversal: 10%
HTTP/Networking: 22% of CVEs
├── SSRF (Server-Side Request Forgery): 35%
├── Request smuggling: 20%
├── Open redirect: 25%
└── Information disclosure: 20%
Authentication/Auth: 18% of CVEs
├── Token validation bypass: 40%
├── Session fixation: 20%
├── Timing attacks: 25%
└── Weak defaults: 15%
Cryptography: 12% of CVEs
├── Weak algorithm defaults: 50%
├── IV reuse / predictable random: 30%
└── Key exposure: 20%
Other categories: 10%
├── Build tools (dev-only): mostly prototype pollution
├── CLI tools: command injection
└── Various utilities: mixed
Category 1: Input Parsing (Highest Risk)
The ReDoS Threat
// ReDoS: Regular Expression Denial of Service
// Attacker provides crafted input that causes exponential regex backtracking
// Result: CPU spike, server hangs, DoS
// Packages historically affected by ReDoS:
// - semver < 7.5.2 (patched)
// - minimatch < 3.0.5 (patched)
// - glob-parent < 5.1.2 (patched)
// - validator.js (multiple advisories)
// - moment.js (parsing specific formats)
// Defense:
// ✅ Use package overrides to force patched versions
// ✅ Set timeouts on regex operations if building custom parsers
// ✅ Validate/sanitize input length before parsing
// ✅ Use a ReDoS scanner: npm install -D safe-regex
import safeRegex from 'safe-regex';
safeRegex(/^(a+)+$/); // returns false: vulnerable to catastrophic backtracking
Prototype Pollution
// Prototype pollution: attacker manipulates Object.prototype
// via deep merge operations on user-supplied data
// Affects: every object in the application silently
// Historically affected packages:
// - lodash < 4.17.21 (patched in 4.17.21)
// - minimist < 1.2.6 (patched)
// - deep-merge (various old versions)
// - jquery (via $.extend())
// Defense:
// Use Object.create(null) for user data containers
const userInput = Object.create(null); // No prototype chain
// Or freeze Object.prototype (defensive but can break libraries):
Object.freeze(Object.prototype);
// Use safe-merge utilities:
import { merge } from 'lodash'; // v4.17.21+ is safe
// or:
const safe = structuredClone(userInput); // Deep clone breaks prototype chain
Category 2: Authentication Packages (Highest Severity)
// jsonwebtoken — historically high-profile vulnerabilities
// CVE-2022-23529: remote code execution via algorithms["HS256"]
// CVE-2022-23540: algorithm confusion attack
// Patched in: jsonwebtoken@9.0.0
// Best practice for JWT in 2026:
import { SignJWT, jwtVerify } from 'jose'; // Modern, audited alternative
// jose: actively maintained, WebCrypto API based, no legacy baggage
// Verify with explicit algorithm:
const { payload } = await jwtVerify(token, secret, {
algorithms: ['HS256'], // Always specify; never allow 'none'
});
// bcrypt / password hashing vulnerabilities:
// bcryptjs: pure JS, some historical timing issues
// argon2: better algorithm (memory-hard), no historical CVEs in Node binding
// scrypt: built into Node.js crypto, use for new projects
import { scrypt, randomBytes, timingSafeEqual } from 'crypto';
// Use built-in Node.js crypto for password hashing when possible
Category 3: HTTP Client/Server Packages
// SSRF: Server-Side Request Forgery
// Your app makes HTTP requests based on user-controlled input
// Attacker points to internal services (localhost, AWS metadata endpoint)
// Vulnerable pattern:
app.get('/proxy', async (req, res) => {
const url = req.query.url; // ❌ User controls URL
const data = await fetch(url); // ❌ Can hit internal services
res.json(data);
});
// Defense:
import { URL } from 'url';
function isSafeUrl(urlStr: string): boolean {
try {
const url = new URL(urlStr);
// Block private/internal addresses
const blocked = ['localhost', '127.0.0.1', '::1', '169.254.169.254'];
if (blocked.some(b => url.hostname === b || url.hostname.endsWith('.internal'))) {
return false;
}
// Only allow HTTP/HTTPS
if (!['http:', 'https:'].includes(url.protocol)) return false;
return true;
} catch {
return false;
}
}
// Open redirect:
// Never redirect to user-controlled URLs without validation
// ❌ res.redirect(req.query.returnUrl)
// ✅ Validate against allowlist of known safe paths
Category 4: Cryptography Packages
// Weak defaults: many packages used weak algorithms historically
// DES, MD5, SHA1 → all broken for security purposes
// Common issues still found in codebases:
// ❌ MD5 for anything security-related
import crypto from 'crypto';
const hash = crypto.createHash('md5').update(password).digest('hex');
// MD5 is broken. Do not use for passwords or security checks.
// ✅ For passwords: argon2 or scrypt (built-in)
// ✅ For content hashes/ETags: SHA256 is fine
const hash = crypto.createHash('sha256').update(content).digest('hex');
// ❌ Predictable random number generation
const token = Math.random().toString(36); // NOT cryptographically secure
// ✅ Cryptographically secure random:
const token = crypto.randomBytes(32).toString('hex'); // Built-in
const id = crypto.randomUUID(); // UUID v4, cryptographically secure
// ❌ Reusing initialization vectors (IV) in encryption
// Always generate a fresh random IV for each encryption:
const iv = crypto.randomBytes(16); // Fresh IV every time
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
Which Packages Get Patched Fastest vs Slowest
Time to patch after CVE disclosure (average, by package category):
< 7 days:
→ Build tools (webpack, vite, rollup)
→ Test frameworks (jest, vitest)
→ Major framework packages (react, vue, angular)
→ Well-funded corporate packages (Next.js, Fastify)
7-30 days:
→ Authentication packages (high priority due to impact)
→ Active utility libraries (lodash, zod)
→ Well-maintained HTTP clients
30-90 days:
→ Mid-tier utility packages
→ Single-maintainer packages with active developers
→ Packages in "maintenance mode" (express: patches still come)
> 90 days or never:
→ Abandoned packages (create-react-app, request, bower)
→ Deep transitive deps that nobody monitors
→ Packages with inactive maintainers
PkgPulse health score factors in:
- Average historical patch time for the maintainer
- Currently open CVEs
- Time since last security advisory
Your Vulnerability Exposure Profile
# Assess your project's security posture:
# 1. Run npm audit
npm audit
# Focus on HIGH and CRITICAL
# LOW/MODERATE: informational, address when convenient
# 2. See the dependency tree for a vulnerability
npm audit --json | jq '.vulnerabilities.semver.nodes'
# Shows: which of YOUR direct deps brings in the vulnerable package
# 3. Check for critical attack surfaces
# High risk: packages handling user input + network I/O
npm ls --all | grep -E "(xml|yaml|csv|html-parser|url-parse|qs)"
# These are high-value targets: input + potential injection
# 4. Look for single-maintainer packages in your tree
npm ls --all 2>/dev/null | head -100
# For each, check: npmjs.com/package/{name} → maintainers count
# 5. Socket security scan (behavioral, not just CVE)
npx @socket/cli report create # Analyzes your package.json
Risk Reduction Checklist
High impact, quick wins:
[ ] Run npm audit — fix all HIGH and CRITICAL findings
[ ] Force override vulnerable transitive deps via package.json overrides
[ ] Replace deprecated packages (request, node-fetch v2, create-react-app)
[ ] Enable Dependabot or Renovate for automated security PRs
Medium effort:
[ ] Audit all packages that parse user input (XML, YAML, CSV, HTML)
[ ] Review authentication packages — are they on latest versions?
[ ] Check cryptography code — any MD5/SHA1/DES usage?
[ ] Add Socket.dev scanning to CI
Ongoing:
[ ] Subscribe to npm security advisories (security.snyk.io)
[ ] Review `npm audit` in CI weekly (via cron workflow)
[ ] Monitor PkgPulse health scores for packages you depend on
[ ] Check single-maintainer packages for activity once per quarter
The Vulnerability Lifecycle: From Disclosure to Patch
Understanding how vulnerabilities move through the npm ecosystem — from initial discovery to the moment npm audit surfaces them — helps calibrate your reliance on any single tool in the detection chain.
The standard disclosure-to-patch pipeline begins with responsible disclosure: security researchers report vulnerabilities privately to the affected maintainer, typically giving them 90 days to release a patch before public disclosure. This is the Google Project Zero standard that most researchers follow, and it means that for the majority of vulnerabilities, a fix is available at or near the time the CVE becomes public.
The npm audit lag is a practical consequence of this pipeline. npm audit shows vulnerabilities that have been formally assigned CVEs and registered with the npm registry's advisory database. There is always a gap between when a vulnerability is privately known and when npm audit surfaces it — and for some long-tail packages, that gap can be substantial if the disclosure process stalls or the maintainer is unresponsive. The practical implication: npm audit is a lagging indicator of known vulnerabilities. It's necessary but not sufficient. Tools like Socket.dev and Snyk maintain faster-updating CVE databases and additionally detect behavioral anomalies — new file system access, new network calls in a package version — that precede formal CVE assignment. These behavioral signals can surface a compromise days before any advisory is published.
The patch adoption timeline shows that the ecosystem responds quickly to high-severity disclosures in well-maintained packages. After a patch is released, most popular packages see 60–80% of their weekly downloads shift to the patched version within 30 days. Teams using Dependabot receive the patch PR automatically within hours of the fixed version being published — the bottleneck is internal review and merge, not awareness. For packages that are lower-visibility or have slower patch adoption, the window of exposure extends significantly. This is why dependency health monitoring — knowing which packages in your tree are maintained by responsive teams — is a leading indicator of your vulnerability exposure profile.
Vulnerability Risk by Development vs Production Dependencies
The blast radius of a vulnerability depends heavily on whether the affected package runs in production or only in development. This distinction matters enormously for prioritization and should shape how you triage npm audit output.
The core insight is that devDependencies don't run in production. A vulnerability in a build tool, test framework, or linter exposes your development environment and CI pipeline — not your users. CI compromise is a real risk (it can lead to supply chain attacks on your own published packages), but the threat model is different from a vulnerability in code that processes user requests in production.
The npm ci --omit=dev production build pattern makes this separation concrete. In Docker production containers, only the dependencies section of package.json is installed. This typically eliminates 50–70% of the packages in the full dependency tree and proportionally reduces the production attack surface.
The production risk profile concentrates around packages that do one or more of the following: process untrusted user input (parsers, validators, template engines), make outbound network calls, have access to the filesystem, or handle authentication and session state. These are the categories where a vulnerability has the highest potential impact — they sit at the boundary between your application and the outside world, or at the boundary between untrusted input and trusted state.
Security review effort should be concentrated on these high-impact categories. A vulnerability in a CSS variable utility used only in the development build is negligible; a vulnerability in your JWT library or your YAML parser is critical regardless of CVSS score. The npm audit --omit=dev flag (combined with --production in some versions) filters audit output to only production-affecting vulnerabilities, giving a cleaner signal about what requires immediate action versus what can be addressed in a scheduled maintenance window.
Why Utility Packages Are the Preferred Supply Chain Attack Target
The supply chain attack vector — compromising the package itself rather than the application consuming it — has evolved substantially since 2020. The targeting pattern is not random. Attackers consistently prefer small, widely depended-upon utility packages over large, high-profile frameworks. Understanding why clarifies how to assess the risk profile of your own dependency tree.
Large, well-known frameworks have too much scrutiny to compromise quietly. A malicious publish to the React or Vue npm registry would be noticed within hours — by automated dependency security scanners, by the security teams at major cloud providers, and by the sheer volume of developers who pull the package daily. The signal-to-noise ratio in the npm download logs for high-profile packages is high enough that anomalous versions get caught quickly. The event-stream compromise in 2018 succeeded precisely because the package was a utility — not glamorous, not widely discussed, but depended upon by millions of projects transitively.
Utility packages have the right attack surface properties: they have real install rates (providing persistence in many codebases), they typically have fewer active contributors and reviewers (making unauthorized publishes easier to achieve through account compromise), and they process real application data (making them useful exfiltration points). A compromised package in the string-manipulation or path-handling category will run in CI environments, developer laptops, and production servers simultaneously — a broad attack surface from a single compromise.
The dependency depth problem compounds this. Modern JavaScript applications routinely have hundreds of transitive dependencies — packages that are dependencies of your dependencies, several layers removed. Most security tooling focuses on direct dependencies, but transitive dependencies represent the larger and less-reviewed attack surface. A single small utility package three layers deep in your dependency tree may be running in your production environment with full access to your process's memory and environment variables, and you may have never audited it. The colors package incident in 2022 — where the maintainer deliberately introduced breaking changes to protest corporate dependency on open source — was a reminder that transitive dependency authors have real leverage over their consumers, whether the intent is malicious or not.
The practical defense against supply chain attacks in the utility package category: npm audit alone is insufficient because it only catches known CVEs, and supply chain compromises typically aren't registered as CVEs until after the fact. Socket.dev and Snyk's behavioral analysis surface new network calls, file system access, and obfuscated code in package versions that weren't present in prior versions — these behavioral signals can catch a compromise before a CVE exists. Locking your dependency tree with npm ci and pinning specific versions (rather than version ranges) in package.json also limits the attack surface, at the cost of manual update management.
Why Some Categories Are Inherently Higher Risk
Vulnerability distribution by category isn't random — it reflects the inherent risk properties of what each category does. Parsers, transport handlers, and cryptographic utilities appear disproportionately in CVE databases because their function requires them to operate at the boundary between trusted and untrusted data, which is where security failures occur.
Input parsers — XML, CSV, YAML, HTML, URL, query strings — are high-risk by definition because their entire job is to convert untrusted external data into trusted internal data structures. Every parsing library that accepts user-controlled input is a potential injection, denial-of-service, or prototype pollution surface. The ReDoS vulnerability class exists entirely in this category because regex-based parsers applied to user-controlled input are the precise combination of conditions needed for catastrophic backtracking. Historical CVE rates for parsing libraries are not a sign of poorly written code — they are a sign that this problem domain is fundamentally adversarial.
HTTP client and server packages sit at the network boundary, which is the other primary attack surface. SSRF attacks are possible anywhere an application makes HTTP requests based on user-controlled input; open redirect vulnerabilities occur anywhere user-controlled URLs are processed and then redirected. These are structural properties of the problem domain, not implementation failures that better code would eliminate. Any HTTP package that gives callers the ability to specify destination URLs is in this risk category regardless of how well it is written.
Authentication packages have a different risk profile: lower CVE frequency than parsers but higher impact per CVE. A parsing library vulnerability may be exploitable only under specific conditions; a JWT algorithm confusion attack may be exploitable against any application using the affected package in its default configuration. The severity concentration in authentication packages — where individual CVEs are rated Critical rather than Medium — reflects the blast radius of a successful exploit. Token validation bypass means credential compromise at scale; timing attacks on password comparison mean offline brute force becomes feasible.
The implication for dependency selection: knowing which category a package falls into should directly inform your evaluation rigor. A new animation library added to a project is categorically different from a new URL parsing utility or a new JWT implementation. The latter two warrant a security review of the specific package — its CVE history, its maintainer's track record, whether it has been professionally audited — that would be excessive for the animation library. Category-aware risk assessment concentrates security review time where it creates the most value.
The Dependency Depth Attack Surface Problem
The typical web application in 2026 has 50 to 100 direct dependencies and 500 to 1,500 transitive dependencies. Most developers are aware of the first number; far fewer have a clear picture of the second. The transitive dependency attack surface is the primary underappreciated risk in npm security.
The depth problem is geometrically worse than it appears. Each package you directly depend on brings its own dependency tree, which brings further sub-trees. The packages three or four levels deep in your dependency tree were not selected by you — they were selected by the maintainers of the packages you selected, each of whom made their own dependency decisions with their own risk tolerances and update cadences. You have no direct visibility into those decisions and typically no review process that would surface them.
The npm audit command only reports vulnerabilities that have been formally disclosed and registered in the npm advisory database. It does not report packages that are actively maintained by compromised accounts, packages that have had malicious code injected but not yet formally reported, or packages that have behavioral anomalies that precede a formal CVE. For transitive dependencies, it also requires that every intermediary dependency have published a version that patches the vulnerability — a chain that can break anywhere. A transitive dependency with a known CVE that its immediate parent hasn't updated against will appear in npm audit output, but many development teams ignore transitive-only audit findings because they can't directly control them.
The most effective structural mitigation is reducing dependency depth. Each package added to a direct dependency list brings its own subtree of transitive dependencies. A team that evaluates whether a new dependency could be replaced by a small amount of in-house code — standard library functions, native browser APIs, a 20-line utility — systematically reduces its total attack surface. This is not a puritanical position against dependencies; it is a calibrated risk assessment that acknowledges the difference between a battle-tested 50-line utility and a 200-package transitive dependency tree.
For packages that can't be eliminated, the mitigation strategy is monitoring. Automated tools — Renovate, Dependabot, Socket.dev — surface security issues in transitive dependencies and provide PR-based update workflows. The key practice is making sure these tools have full visibility into your package-lock.json or pnpm-lock.yaml, not just your package.json, so transitive vulnerabilities are captured.
Category Risk Should Inform Dependency Selection, Not Just Auditing
Most npm security guidance focuses on what to do after a vulnerability is discovered — run npm audit, apply patches, use Dependabot. Less attention is paid to using category risk as a criterion at the point of dependency selection, before a vulnerability exists. This is a gap in how most teams approach the problem.
The reasoning: if parsers, transport handlers, and authentication packages are known to have higher vulnerability density and higher severity-per-CVE, then selecting a dependency in those categories from a poorly maintained or opaquely governed package is a predictably higher-risk decision than selecting the same type of package from a well-maintained, audited source. The vulnerability hasn't occurred yet, but the structural conditions for it are already present.
Applying this logic to dependency selection means developing a tiered evaluation process. For packages in low-risk categories — utilities that process no user input, perform no network operations, and have no filesystem access — a quick health check (recent releases, responsive maintainer, reasonable download volume) is sufficient. For packages in high-risk categories — anything that parses user-controlled input, makes HTTP requests, handles credentials, or wraps cryptographic operations — the evaluation should include CVE history review, maintainer track record on security response, whether the package has been through a professional security audit, and whether the organization behind it has a published security disclosure policy.
This category-aware approach also informs the "build vs buy" decision. A small input sanitization utility with a single maintainer and no security audit history is a candidate for replacement with either a battle-tested alternative (like DOMPurify for HTML sanitization) or a small amount of in-house code that you own entirely. The argument against writing your own cryptography is well-established — but the argument against writing your own 20-line URL validator is less clear-cut when the alternative is adding a dependency in a high-risk category from an undervetted source. The make-versus-buy analysis should factor in category risk explicitly.
How SOC 2 Compliance Shapes Enterprise npm Security Posture
The entry of formal compliance frameworks into npm security posture is one of the more significant shifts in how larger organizations approach dependency management. SOC 2 compliance — specifically Type II, which requires evidence of continuous control operation — has made dependency vulnerability tracking a documented, audited process rather than an informal best practice.
SOC 2's Change Management control typically requires that software changes, including dependency updates, go through an approval workflow with evidence of security review. For most organizations, this is interpreted to mean that npm audit findings above a defined severity threshold must be remediated within a defined SLA — often 30 days for High severity, 90 days for Medium — with documentation showing the finding, the remediation action, and the verification. The specific thresholds and SLAs vary by auditor and organization, but the effect is consistent: security updates that previously might have waited for a convenient quarterly maintenance window must now be shipped on a defined schedule with paper evidence.
The practical effect on dependency selection is significant. Teams under SOC 2 pressure begin evaluating packages not just on technical merit but on security track record and update cadence — factors that directly affect their ability to meet remediation SLAs. A package with a history of long-unpatched CVEs or slow security response is a liability under compliance frameworks because it increases the likelihood of a finding that requires emergency patching outside normal release cycles. This pressure drives behavior toward well-maintained packages from established vendors even when alternatives might be technically superior, because the established packages have better defined security response processes and more predictable patch timelines.
The compliance angle also explains why enterprise security tooling like Snyk, Socket.dev, and JFrog Xray has seen such rapid adoption at the enterprise level. These tools produce the artifacts that SOC 2 auditors ask to see: vulnerability scans with timestamps, remediation tracking, policy configuration evidence, and continuous monitoring history. The tools are not simply detecting vulnerabilities — they are generating the compliance documentation that organizations need to demonstrate continuous control operation.
Monitor security scores and vulnerability data for npm packages at PkgPulse.
See also: Lit vs Svelte and npm Supply Chain Security Guide 2026, How to Secure Your npm Supply Chain in 2026.
See the live comparison
View npm vs. pnpm on PkgPulse →