Like a bouncer who faints when someone shows a fake ID labeled '__proto__'
CVE-2026-19481 affects @fastify/busboy versions >= 1.0.0 and < 3.2.1, the streaming multipart form-data parser used across the Node.js ecosystem — most notably as a transitive dependency of undici and, by extension, Node.js's built-in fetch(). The parser stores part-header names in a plain JavaScript object. When a crafted multipart request includes a header named __proto__ or constructor, the lookup resolves to an inherited prototype value (not an array), and the subsequent .push() call throws an unhandled TypeError. An unauthenticated remote attacker can crash any Node.js process that pipes incoming multipart/form-data through Busboy without an error handler.
The vendor's HIGH / 7.5 rating is defensible from a pure CVSS-vector standpoint — unauthenticated, network-reachable, low-complexity, high availability impact. But it overstates the real-world pain. The blast radius is a single Node.js worker process crash, not data loss or code execution. Production deployments behind PM2, systemd, or Kubernetes will auto-restart the process in under a second. Applications using the idiomatic req.pipe(busboy) pattern surface the error as an event rather than a hard crash. The fix (upgrade or add an error listener) is trivial. This is a nuisance DoS, not a service-killer.
4 steps from start to impact.
Identify multipart upload endpoint
multipart/form-data — a file upload route, form submission handler, or API ingestion endpoint. No authentication is required. Endpoints are trivially discoverable via documentation, crawling, or fuzzing Content-Type headers.- Target application uses @fastify/busboy (directly or via @fastify/multipart, undici, or Node built-in fetch with FormData parsing)
- At least one route accepts multipart/form-data
- Not every Node.js service exposes multipart endpoints — pure JSON APIs are unaffected
- Reverse proxies or WAFs may reject malformed multipart payloads before they reach the app
Send crafted multipart request with prototype header
__proto__ or constructor. This is a single HTTP request; no tooling beyond curl is required. Example: curl -X POST -F '__proto__=x' https://target/upload. The malformed header name collides with JavaScript's Object prototype chain.- Network reachability to the target endpoint
- Some WAFs (e.g., AWS WAF, Cloudflare) with managed rulesets may flag
__proto__in request bodies as prototype-pollution attempts - Rate limiting slows repeated crash attempts
__proto__ or constructor in multipart field names; Snyk, npm audit, and Socket.dev flag the vulnerable @fastify/busboy versionParser throws unhandled TypeError
HeaderParser, the code does this.header[h].push(val). When h is __proto__, this.header[h] resolves to Object.prototype (an object, not an array), and .push() throws TypeError: this.header[h].push is not a function. If the caller does not catch this error, the Node.js process exits.- Application does not attach an error listener to the Busboy stream
- No global uncaughtException handler
- Idiomatic Fastify multipart integration surfaces this as an error event, not a hard crash
- Well-written Node.js services have process-level error handlers
- Process managers (PM2, systemd, Kubernetes) auto-restart crashed workers within ~1 second
TypeError: this.header[h].push is not a function in busboy's header parserTemporary denial of service
- Attacker can send requests faster than the process restarts
- Clustered deployments (Node cluster, Kubernetes pods, PM2 cluster mode) limit blast to one worker at a time
- Rate limiting, IP blocking, or CDN-level DDoS protection can throttle repeat attempts
- No data exfiltration, no code execution, no persistence — attacker gains nothing beyond transient unavailability
The supporting signals.
| In-the-wild exploitation | No known exploitation. Not listed on CISA KEV. No campaigns or threat actor usage reported as of 2026-08-13. |
|---|---|
| Proof of concept | Trivially reproducible: curl -X POST -F '__proto__=x' http://target/upload. No weaponized tooling needed — the PoC is a one-liner. Reported by researcher kq5y. |
| EPSS | Not yet scored (advisory published 2026-08-12). Expected to be low given DoS-only impact and no weaponized exploitation chain. |
| KEV status | Not listed. No CISA KEV entry as of 2026-08-13. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — 7.5 HIGH. Network-reachable, no auth, availability-only impact. No confidentiality or integrity impact. |
| Affected versions | @fastify/busboy >= 1.0.0, < 3.2.1. This spans all major releases since the Fastify fork of the original busboy library. |
| Fixed version | @fastify/busboy 3.2.1 (released 2026-08-12) |
| Exposure population | @fastify/busboy has ~13–21 million weekly npm downloads, largely driven by its role as a transitive dependency of undici and Node.js's built-in fetch(). However, only apps that *parse* multipart/form-data via Busboy are vulnerable — pure JSON APIs and non-multipart services are unaffected. |
| Disclosure date | 2026-08-12 (coordinated disclosure via GitHub Security Advisory GHSA-x8mw-p69m-v3mx) |
| Reporter | kq5y (reporter); mcollina (Fastify maintainer, remediation); UlisesGascon (review) |
noisgate verdict.
The single most decisive downgrade factor is blast radius limited to transient process crash — the vulnerability yields no code execution, no data access, and no persistence; a crashed Node.js worker auto-restarts in under a second in any production-grade deployment. The availability-only impact against a self-healing runtime makes the vendor's HIGH rating overweight for real-world enterprise risk.
Why this verdict
- DoS-only, no RCE or data impact. The worst outcome is a process crash — no confidentiality or integrity loss. CVSS correctly scores C:N/I:N, but a 7.5 label implies urgency disproportionate to the real consequence.
- Auto-restart neutralizes persistence. Production Node.js deployments use PM2, systemd, Kubernetes, or cluster mode. A crashed worker restarts in <1 second. The attacker must sustain a stream of requests to maintain degradation, which is indistinguishable from a volumetric DoS that your existing DDoS defenses already handle.
- Attack surface is narrower than the download count suggests. @fastify/busboy has ~20M weekly downloads because it's a transitive dep of undici/Node fetch, but the vulnerability only fires when the application *actually parses multipart/form-data through Busboy*. Pure JSON APIs, GraphQL services, and SSR apps are unaffected even if the package is in their
node_modules. - Trivial workaround available. Adding
.on('error', handler)to the Busboy stream or wrapping in try/catch prevents the crash entirely, without upgrading. - Role multiplier: @fastify/busboy is an application-layer npm library, not a canonical high-value-role component. (a) *Low-value role* (dev tooling, test harness): crash is irrelevant. (b) *Typical role* (line-of-business API server): crash restarts automatically, impact is seconds of downtime. (c) *High-value role* (CI/CD pipeline accepting artifact uploads, API gateway): even here the blast radius is a single worker process crash with auto-restart — it does not escalate to fleet compromise, identity takeover, or supply-chain pivot. No deployment role produces an outcome that triggers the HIGH floor.
Why not higher?
To warrant HIGH, the vulnerability would need to produce sustained, unrecoverable denial of service or affect a component where even brief downtime causes cascading failure (e.g., a domain controller, hypervisor, or PKI service). This bug crashes one worker process that auto-restarts in under a second. The attacker gains no foothold, no data, and no lateral movement capability. The attack is also fully mitigable by a one-line error handler without even upgrading the package.
Why not lower?
Despite the mitigations, this is still an unauthenticated, network-reachable crash with zero interaction required and trivial exploit complexity — a single curl command. The affected package has massive install-base reach, and not every deployment will have proper error handling or process supervision. For single-process Node.js deployments without restart logic (development servers promoted to production, serverless cold-start scenarios), the crash is real and disruptive. That practical exposure keeps it above LOW.
What to do — in priority order.
- Add error handlers to all Busboy stream instances — Attach
.on('error', (err) => { /* log and respond 400 */ })to every Busboy instance in your codebase. This prevents the TypeError from becoming an uncaught exception. This is a code-level change deployable immediately as a compensating control; no package upgrade required. - Deploy WAF rule blocking
__proto__andconstructorin multipart field names — Configure your WAF (AWS WAF, Cloudflare, ModSecurity) to reject multipart/form-data requests containing__proto__,constructor, or__defineGetter__as field names. This blocks the attack at the network edge before it reaches the application. Deploy within the noisgate remediation window of 365 days, though sooner is better. - Ensure process supervision is configured — Verify that all Node.js services run under PM2, systemd, Kubernetes, or equivalent process manager with automatic restart on crash. This limits the blast radius of any uncaught exception to a sub-second restart.
- Upgrade to @fastify/busboy >= 3.2.1 — The definitive fix. Run
npm update @fastify/busboyor pin>=3.2.1in your lockfile. For @fastify/multipart users, update to the latest version that pulls in the patched busboy. This is the remediation action under the noisgate 365-day remediation SLA for MEDIUM.
- Rate limiting alone — while it slows repeated crashes, a single request is enough to crash one worker. Rate limiting helps but doesn't prevent the first crash.
- Input validation on the application route handler — the crash occurs inside Busboy's internal header parser *before* your route handler's validation logic runs, so application-level field-name validation won't intercept it.
- Upgrading Node.js alone — the vulnerability is in the npm package @fastify/busboy, not in Node.js core. Even though Node.js bundles undici (which depends on @fastify/busboy), the bundled version is only used for
fetch()FormData parsing, not for Express/Fastify multipart routes. You must update the npm package independently.
Crowdsourced verification payload.
Run this on any host where a Node.js application using @fastify/busboy is deployed. Execute as the application user or any user with read access to the project's node_modules. Example: bash check_cve_2026_19481.sh /opt/myapp
#!/usr/bin/env bash
# CVE-2026-19481 checker for @fastify/busboy
# Usage: bash check_cve_2026_19481.sh /path/to/node/project
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-.}"
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=$(grep '"version"' "$PKG_JSON" | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/')
if [ -z "$VERSION" ]; then
echo "UNKNOWN — could not parse version from $PKG_JSON"
exit 2
fi
# Compare version against fixed version 3.2.1
# Affected: >= 1.0.0, < 3.2.1
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3 | cut -d- -f1)
if [ "$MAJOR" -lt 1 ]; then
echo "PATCHED — @fastify/busboy $VERSION is below affected range (< 1.0.0)"
exit 0
elif [ "$MAJOR" -gt 3 ]; then
echo "PATCHED — @fastify/busboy $VERSION is above fixed version"
exit 0
elif [ "$MAJOR" -eq 3 ] && [ "$MINOR" -gt 2 ]; then
echo "PATCHED — @fastify/busboy $VERSION is above fixed version"
exit 0
elif [ "$MAJOR" -eq 3 ] && [ "$MINOR" -eq 2 ] && [ "$PATCH" -ge 1 ]; then
echo "PATCHED — @fastify/busboy $VERSION (>= 3.2.1)"
exit 0
else
echo "VULNERABLE — @fastify/busboy $VERSION (CVE-2026-19481, fix in 3.2.1)"
exit 1
fiIf you remember one thing.
@fastify/busboy upgrade to >= 3.2.1 within your next regular patching cycle. If you run internet-facing multipart upload endpoints on single-process Node.js without process supervision, add .on('error', handler) to your Busboy instances this week as a precaution, but this is not a drop-everything patch. Prioritize your CRITICAL and HIGH queue items first; slot this into your next scheduled dependency update sprint.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.