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
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. |
noisgate verdict.
The single most decisive factor keeping this at HIGH is the trivially exploitable unauthenticated CPU-hang with zero-click, single-request lethality against any Node.js process accepting multipart uploads. The narrow affected version window (3.1.0–3.2.0 only) and availability-only impact ceiling prevent escalation to CRITICAL.
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.
What to do — in priority order.
- Enforce RFC 2046 boundary length at the reverse proxy — RFC 2046 limits multipart boundaries to 70 characters. Configure your reverse proxy (nginx, HAProxy, Envoy, Cloudflare WAF) to reject requests with
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. - Upgrade @fastify/busboy to 3.2.1 — Drop-in fix with no breaking changes. Run
npm update @fastify/busboyor pin^3.2.1inpackage.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. - Run Node.js in cluster mode with health checks — Use
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. - Rate-limit multipart endpoints by source IP — Configure your WAF or API gateway to rate-limit POST requests to multipart-accepting endpoints. This raises the attacker's cost for sustained DoS but does not prevent single-request hangs.
- 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.
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 0If you remember one thing.
@fastify/busboy 3.1.0–3.2.0 this week. The fix is a one-line version bump to 3.2.1 — no breaking changes, no migration. For services you can't redeploy immediately, enforce the RFC 2046 boundary length limit (≤70 chars) at your reverse proxy as a compensating control. Per the noisgate mitigation SLA for HIGH severity, have the compensating control deployed within 30 days; per the noisgate remediation SLA, complete the actual package upgrade across all services within 180 days. Given that exploitation is trivial and the patch is a drop-in, there's no reason to ride the full SLA window — target the upgrade in your next sprint. No active exploitation has been observed yet, but the PoC is derivable from the advisory text in minutes, so the clock is ticking.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
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.