A single crafted query string is enough to crash a Node.js service running Express. CVE-2022-24999 is a prototype pollution vulnerability in qs, the query-string parsing library that Express uses internally to turn req.query into a JavaScript object. By sending specially nested parameters, an unauthenticated attacker can pollute Object.prototype, corrupting shared object behavior across the application and, in the most common exploitation path, throwing unhandled exceptions that take the process down. Because qs sits underneath Express, body-parser, superagent, and dozens of other widely used packages, the practical blast radius extends far beyond apps that call qs directly — it touches almost every Express-based service that accepts query parameters from the outside world.
This is a denial-of-service bug, not a remote-code-execution bug, but the "just a DoS" framing understates the risk. Public-facing Express APIs, admin panels, and internal microservices that trust upstream traffic can all be knocked offline with a single HTTP request, no authentication or user interaction required. For teams running Node.js in production at scale, that's a meaningful availability risk sitting quietly in the dependency tree.
What's Actually Vulnerable
qs parses bracket-notation query strings like a[b][c]=d into nested objects, and it parses a[]=1&a[]=2-style parameters into arrays. Versions before the fix failed to fully guard against keys that resolve to __proto__ when parsing certain array and object combinations, particularly around how qs handles conversion between sparse arrays and plain objects during recursive key parsing. An attacker who crafts a query string that reaches this code path can assign properties onto Object.prototype itself rather than onto the intended request object. Once Object.prototype is polluted, every other object in the running process inherits the tampered property, and code elsewhere in the application that assumes a "clean" object (or that later throws when it encounters an unexpected inherited property) can crash the event loop.
This is distinct from — and a resurgence of — the class of qs prototype pollution issues tracked in earlier CVEs (CVE-2017-1000048 and CVE-2021-23413 among them). The maintainers had already added __proto__ denylisting; CVE-2022-24999 demonstrated that the denylist could still be bypassed through the array-to-object conversion logic, which is a good reminder that prototype pollution guards implemented as key blocklists are notoriously easy to route around through parser edge cases.
Affected Versions and Components
The vulnerability affects the qs npm package prior to the following patched releases: 6.2.4, 6.3.3, 6.4.1, 6.5.3, 6.6.1, 6.7.3, 6.8.3, 6.9.7, and 6.10.3. If your qs version falls below the patched release in its own minor line, it's affected.
Because qs is a transitive dependency, direct usage isn't the only exposure path:
- Express parses
req.queryusing qs by default, so any Express app is exposed unless its resolved qs version is patched. - body-parser, superagent, webpack-dev-server, and a long tail of Express middleware and HTTP client libraries pull in qs transitively.
- Monorepos and microservice fleets frequently end up with several different qs versions installed side by side across services, since npm/yarn dedupe by semver range, not by patch status — meaning one team can ship a fix while three other services still carry the vulnerable version buried three or four levels deep in
node_modules.
This is the pattern that makes qs-style advisories so operationally painful: the fix is trivial, but finding every place the vulnerable code actually lives — and confirming which of those locations can actually be reached with attacker-controlled input — takes real dependency-graph work.
CVSS, EPSS, and KEV Context
CVE-2022-24999 carries a CVSS v3.1 base score of 7.5 (High), with a vector reflecting a network-exploitable, low-complexity attack requiring no privileges and no user interaction, resulting in a high impact to availability and no direct impact to confidentiality or integrity (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H). That scoring reflects the dominant real-world outcome: an application crash, not data theft or code execution.
EPSS (Exploit Prediction Scoring System) has historically placed this CVE in the low-probability range typical of DoS-class prototype pollution bugs rather than the high-urgency tier reserved for RCE or auth-bypass flaws — but EPSS scores drift over time as scanning activity and public PoCs circulate, so treat any point-in-time percentile as directional rather than definitive and re-check it against current feeds before deciding on remediation priority.
CVE-2022-24999 has not appeared on CISA's Known Exploited Vulnerabilities (KEV) catalog as of this writing. Absence from KEV is not a green light to deprioritize it, though: qs's ubiquity in the Node.js ecosystem means opportunistic scanning for exploitable query-parsing endpoints is common, and the barrier to weaponizing a working payload is low once the vulnerable code path is confirmed reachable.
Timeline
- Vulnerability identified: Security researchers reported a bypass of qs's existing
__proto__denylist to the maintainers, showing that array/object conversion during recursive parsing could still be abused to write ontoObject.prototype. - Patch released: The qs maintainers shipped fixes across all actively supported minor lines (6.2.x through 6.10.x), consolidating on 6.10.3 as the current patched release.
- Advisory published: GitHub Security Advisory GHSA-hrpp-h998-j3pp documented the issue and the full matrix of patched versions.
- CVE assigned and published: CVE-2022-24999 was registered and published in the NVD, with downstream advisories issued for Express and other packages that bundle qs as a dependency.
- Ecosystem remediation (ongoing): Express and other maintainers bumped their bundled qs versions in subsequent releases, but because qs is resolved independently by every consumer's lockfile, a large population of applications continued running vulnerable transitive copies well after the upstream fix — a pattern that persists in dependency scans today.
Remediation Steps
- Identify every resolved qs version in your dependency tree, not just direct dependencies. Run
npm ls qs(oryarn why qs/pnpm why qs) across each service and lockfile — a single monorepo can easily surface three or four different qs versions at different depths. - Upgrade qs to a patched release in each affected minor line (6.2.4, 6.3.3, 6.4.1, 6.5.3, 6.6.1, 6.7.3, 6.8.3, 6.9.7, or 6.10.3+). If a transitive dependency hasn't bumped its own qs pin, use
overrides(npm),resolutions(yarn), orpnpm.overridesto force the patched version without waiting on an upstream maintainer. - Upgrade Express itself to a release that bundles a patched qs version, since Express's own
query parserdefault depends on the version it ships with. - Bound query-string parsing defensively: set conservative
parameterLimitanddepth/arrayLimitoptions wherever qs is configured directly, reducing the size and nesting of payloads the parser will process regardless of patch status. - Add a startup-time guard that freezes or validates
Object.prototypeshape, or use a dedicated prototype-pollution-hardening middleware, as defense in depth against this and future pollution bugs in any parsing library in the stack. - Validate query parameters against a schema (zod, joi, ajv) before they reach business logic, so unexpected keys — polluted or not — never propagate deeper into the application.
- Re-scan after patching to confirm no duplicate, unpatched qs copies remain elsewhere in the dependency graph — a common failure mode is fixing the top-level dependency while a nested copy pulled in by an unrelated package stays vulnerable.
How Safeguard Helps
Dependency advisories like CVE-2022-24999 generate a lot of noise because qs shows up dozens of times across a typical service fleet, and most of those instances aren't actually exploitable. Safeguard's reachability analysis traces whether attacker-controlled query-string input actually flows into the vulnerable qs parsing path in your specific codebase, so your team can triage the handful of genuinely exposed services instead of chasing every SBOM hit. Griffin AI reviews each finding in context, explains the exploitation path in plain language, and ranks it against your actual risk posture rather than a generic CVSS score. Safeguard's SBOM generation and ingest continuously map every transitive copy of qs across your repos and containers — including the buried, mismatched versions that manual npm ls audits routinely miss — and surface them in one place. When a fix is confirmed safe, Safeguard opens auto-fix pull requests that bump qs (or add the necessary override) directly in the affected manifests, turning a multi-team remediation slog into a same-day merge.