One crafted web address can take a busy Node.js server offline for tens of seconds at a time, and the cause is a package most teams have never heard of and never install on purpose. decode-uri-component is a small helper that turns percent-encoded text back into readable characters. It rides into millions of applications as a dependency of query-string, one of the most widely used URL parsers on npm, and it draws more than 20 million downloads a week on its own.
On June 30, 2026, the patch vendor Seal Security assigned CVE-2026-45822 to a denial-of-service flaw in every release up to and including 0.4.1. It is rated 6.6 (medium) and filed under uncontrolled resource consumption and inefficient algorithmic complexity. There is no confidentiality or integrity impact, and no public exploit has been reported. What it costs you is availability, and on a single-threaded runtime like Node.js, that is enough to matter.
What actually breaks
The decoder splits its input apart on every percent sign and then re-decodes each piece. The catch is that the work grows far faster than the input does. Hand it a string packed with percent-encoded fragments and the parse time climbs super-linearly, so a request that should finish in microseconds instead spins the processor for seconds. Because Node.js runs your request handlers on a single event loop, that spin freezes every other request in flight. One attacker, one oversized query string, and the whole process stops answering.
| Crafted input | Approx. parse time |
|---|---|
| 200 fragments | ~0.7 seconds |
| 700 fragments | ~6 seconds |
| 1,400 fragments | ~33 seconds |
The measurements above come from the advisory itself: 200 fragments already cost about 0.7 seconds, 700 push past six, and 1,400 reach roughly 33. A modest amount of crafted input, repeated across a handful of connections, keeps a worker pinned with very little effort on the attacker's side.
We have been here before
This is the second denial-of-service CVE in the same small package. In 2022, CVE-2022-38900 flagged the same class of slowdown, and the maintainer shipped 0.2.1 to address it. That release narrowed the problem without closing it. The new finding shows the decoder still degrades badly on hostile input, which is why the real remedy this time was not another tweak to the old logic but a full rewrite. Version 0.5.0, published to npm on June 29, 2026, replaces the recursive splitting with a single-pass scanner that reads the string once, in linear time.
How to actually get the fixed version
Here is the part that catches teams out. Patching is not as simple as npm update, because almost nobody depends on this package directly. query-string at version 9.4.1 declares its dependency as "decode-uri-component": "^0.4.1". For a release below 1.0.0, that caret range means the 0.4.x line only, anything up to but not including 0.5.0. The fixed release sits one step outside the window your dependency tree is allowed to choose, so a routine update quietly leaves you on the vulnerable 0.4.1.
To pull the patched version today, force it with a dependency override instead of waiting for the upstream range to widen. In npm or pnpm, add an overrides entry; in Yarn, a resolutions entry, each pinning decode-uri-component to 0.5.0. Reinstall, then confirm the resolved version in your lockfile rather than trusting the command to have done it. If you maintain a package that depends on query-string, watch for a release that loosens its range so the fix can flow downstream on its own.
If you cannot patch immediately
The exposure is real only where attacker-controlled text reaches the decoder, which in practice means query strings and URL fragments parsed on the server. Cap the size of incoming query strings at the edge: a reverse proxy or web application firewall rule that rejects absurdly long URLs removes the cheap version of this attack. Watch for request handlers that suddenly run for whole seconds, and for event-loop lag climbing under load. Those are the signs that someone is feeding your parser oversized input on purpose.
None of this is dramatic on its own. It is a medium-severity bug in an unglamorous utility. But it is a clean example of how supply-chain risk actually lands: not through the package you picked, but through the one it quietly picked for you, governed by a version range you never set.