A single misaligned byte in the boundary lookup table turns your API server's event loop into a hamster wheel that never stops
CVE-2026-19484 is an infinite-loop denial-of-service in @fastify/busboy versions 3.1.0 through 3.2.0. The multipart boundary-search algorithm uses a lookup table internally. When an attacker sends a multipart request with a boundary of exactly 252 bytes, the search needle becomes 256 bytes, causing a table index to wrap to zero. This traps the parser in a CPU-bound loop on the Node.js event loop — a single malicious request is enough to hang the entire process. No authentication is required; the parser runs before any application middleware. Any service accepting multipart/form-data via @fastify/multipart, Fastify, or direct @fastify/busboy usage on the affected versions is vulnerable. The fix is in version 3.2.1, released August 12, 2026.
The vendor's HIGH / 7.5 rating is fair but slightly generous. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) correctly reflects unauthenticated network-reachable DoS. However, two factors temper real-world risk: (1) the affected version window is narrow — only two minor versions (3.1.0–3.2.0) are vulnerable, so a large portion of the ~22 million weekly download base still on 2.x or earlier 3.0.x is unaffected; and (2) the impact ceiling is availability loss, not code execution or data breach. We downgrade fractionally to 7.0 to reflect the constrained blast radius while respecting the trivial exploitability.
4 steps from start to impact.
Identify a target accepting multipart/form-data
@fastify/busboy ≥3.1.0 qualifies. Shodan/FOFA Server: fastify headers or framework fingerprinting narrows the search.- Target runs @fastify/busboy 3.1.0–3.2.0
- Target endpoint accepts multipart/form-data Content-Type
- Many deployments still run 2.x or 3.0.x — only 3.1.0+ is affected
- Some reverse proxies enforce RFC 2046 boundary length limits (≤70 chars), which would reject the 252-byte boundary before it reaches the app
Craft a multipart request with a 252-byte boundary
Content-Type: multipart/form-data; boundary=<252 chars>. This is trivial — no special tooling beyond curl is needed. The 252-byte boundary plus the -- prefix and CRLF creates a 256-byte search needle internally. No weaponized tool required; a one-liner suffices: curl -X POST -H 'Content-Type: multipart/form-data; boundary=AAAA...252 chars' --data-binary $'--AAAA...\r\n' target/upload.- Network reachability to the target endpoint
- Rate limiting or connection timeouts at the load balancer may cap the damage window
- Some CDNs (Cloudflare, AWS ALB) may normalize or reject oversized boundary values
Node.js event loop hangs indefinitely
- Request reaches the busboy parser (pre-middleware, pre-auth)
- Cluster mode (pm2, node cluster) requires one poisoned request per worker
- Process managers with health checks will restart hung workers, limiting downtime to seconds
- Kubernetes liveness probes will restart the pod
Service unavailability achieved
- Attacker can sustain a low rate of requests (1 per worker per restart cycle)
- IP-based rate limiting or connection limits at the edge mitigate sustained attacks
- Automated scaling (HPA in k8s) can outpace a single-source attacker
boundary= values exceeding 70 bytes. This neutralizes the attack vector entirely. Deploy within the noisgate HIGH mitigation SLA of 30 days, though given trivial exploitability, aim for days not weeks.npm update @fastify/busboy or pin ^3.2.1 in package.json. This is the definitive remediation. Target deployment within the noisgate HIGH remediation SLA of 180 days, but realistically push this in your next release cycle.node:cluster, PM2, or Kubernetes liveness probes with tight thresholds (e.g., 5-second event loop lag). This limits the blast radius of a single hung worker and auto-restarts affected processes. Not a fix — just damage containment.- Generic request-size limits (e.g.,
client_max_body_size) — the malicious payload is tiny (a few hundred bytes in the Content-Type header). Body size limits won't catch it. - Application-layer auth middleware — the busboy parser runs *before* your auth middleware in the Fastify request lifecycle. Adding authentication to the endpoint does not prevent the parser from triggering on the raw request.
- Node.js
--max-old-space-sizememory limits — this is a CPU-bound loop, not a memory exhaustion. Memory limits are irrelevant.
The supporting signals.
| In-the-Wild Exploitation | No known exploitation as of 2026-08-13. Not listed on CISA KEV. Disclosed same day as patch — exploitation window just opened. |
|---|---|
| Proof of Concept | The GHSA advisory (GHSA-xjh9-v7x6-24jw) includes the trigger condition in its description. A working PoC is trivial — a single curl command with a 252-char boundary. No public exploit repo found yet, but weaponization is minutes away. |
| EPSS Score | Not yet scored (CVE disclosed 2026-08-13). Expected to be moderate (~0.15–0.30) given unauthenticated network DoS with trivial complexity. |
| KEV Status | Not listed. No federal directive to patch. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — unauthenticated remote DoS, no scope change, availability-only impact. The vector is accurate. |
| Affected Versions | @fastify/busboy ≥3.1.0, <3.2.1. Versions 1.x and 2.x are not affected. Version 3.0.x is not affected. |
| Fixed Version | 3.2.1 (released August 12, 2026). Drop-in replacement — no breaking API changes. |
| Exposure / Install Base | ~22 million weekly npm downloads. @fastify/busboy is a dependency of undici and is used indirectly by Node.js core's built-in fetch(). However, the server-side multipart parsing path (where this bug lives) is only exercised by applications explicitly parsing incoming multipart/form-data, not by client-side fetch() consumers. |
| Disclosure Date | 2026-08-13 (coordinated disclosure, patch available same day) |
| Reporter | Matteo Collina (mcollina), Fastify core maintainer. Self-reported and self-patched. |
Sources.
- GHSA-xjh9-v7x6-24jw — @fastify/busboy DoS via oversized boundary
- @fastify/busboy v3.2.1 release (security fix)
- @fastify/busboy npm package
- CWE-835: Loop with Unreachable Exit Condition
- FIRST EPSS — Exploit Prediction Scoring System
- CISA Known Exploited Vulnerabilities Catalog
- @fastify/busboy GitHub repository
Why this verdict
- Unauthenticated, zero-click DoS — a single crafted HTTP request hangs the entire Node.js event loop. No credentials, no user interaction, no chaining required. This anchors the score at HIGH.
- Narrow affected version range — only 3.1.0 through 3.2.0 are vulnerable. The 2.x line and 3.0.x are unaffected, which materially reduces the exposed population from the ~22M weekly download figure. This is the primary downward pressure from the vendor's 7.5.
- Availability-only impact ceiling — there is no path from this bug to code execution, data exfiltration, or privilege escalation. The worst outcome is service downtime, which bounds the maximum severity.
- Role multiplier:
@fastify/busboyis a userland npm library consumed by Node.js web applications. Typical deployment roles are API servers and web backends (role b). It does not run on domain controllers, hypervisors, identity providers, or kernel-mode agents. In a high-value role scenario (e.g., a Node.js API gateway fronting critical services), successful exploitation causes service unavailability — significant but not fleet compromise, domain takeover, or supply-chain pivot. The blast radius is host/service-level, not domain/fleet-level, so the floor remains HIGH (not CRITICAL). - Trivial weaponization — the advisory description is essentially the PoC. Any attacker who can read the GHSA can reproduce it in under 5 minutes with
curl. This prevents any downgrade below HIGH.
Why not higher?
This is not CRITICAL because the impact is limited to availability — there is no code execution, no data breach, no lateral movement. The affected component is a userland npm library, not a hypervisor, identity provider, or kernel-mode agent. The narrow version window (two minor releases) further limits the reachable population. DoS-only vulnerabilities, even trivially exploitable ones, do not meet the CRITICAL floor unless they affect safety-critical OT/ICS systems or enable cascading infrastructure failure.
Why not lower?
This cannot be MEDIUM because the exploitation is completely unauthenticated, requires zero user interaction, has trivial attack complexity, and the PoC is effectively public. A single small HTTP request can take down an entire Node.js process. The asymmetric cost (bytes in vs. full service hang) and the lack of any prerequisite compromise step mean there is no meaningful friction to justify a downgrade to MEDIUM.
Crowdsourced verification payload.
Run this on any host with node and npm installed to check whether the installed @fastify/busboy version is in the vulnerable range. No special privileges required. Example: bash check_busboy_cve_2026_19484.sh /path/to/your/project
#!/usr/bin/env bash
# check_busboy_cve_2026_19484.sh
# Checks whether @fastify/busboy is in the vulnerable range 3.1.0–3.2.0
# Usage: bash check_busboy_cve_2026_19484.sh [project_dir]
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-.}"
if [ ! -d "$PROJECT_DIR/node_modules" ]; then
echo "UNKNOWN — no node_modules found in $PROJECT_DIR. Run npm install first."
exit 2
fi
PKG_JSON="$PROJECT_DIR/node_modules/@fastify/busboy/package.json"
if [ ! -f "$PKG_JSON" ]; then
echo "UNKNOWN — @fastify/busboy not found in $PROJECT_DIR/node_modules."
exit 2
fi
VERSION=$(node -e "console.log(require('$PKG_JSON').version)" 2>/dev/null)
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not read version from $PKG_JSON."
exit 2
fi
# Parse semver
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
# Vulnerable: >=3.1.0, <3.2.1
if [ "$MAJOR" -eq 3 ]; then
if [ "$MINOR" -eq 1 ]; then
echo "VULNERABLE — @fastify/busboy $VERSION is in the affected range (3.1.0–3.2.0). Upgrade to 3.2.1."
exit 1
elif [ "$MINOR" -eq 2 ] && [ "$PATCH" -eq 0 ]; then
echo "VULNERABLE — @fastify/busboy $VERSION is in the affected range (3.1.0–3.2.0). Upgrade to 3.2.1."
exit 1
fi
fi
echo "PATCHED — @fastify/busboy $VERSION is not in the vulnerable range."
exit 0