It's like picking a lock on a door that's already welded shut from the other side
CVE-2026-77063 is a race condition in multer, the dominant file-upload middleware for Express.js / Node.js, affecting all versions before 2.3.0. When an application configures *both* an asynchronous fileFilter callback *and* a fileSize limit on multer.diskStorage() or multer.memoryStorage(), the async filter's resolution can race against the size-limit enforcement path. If the race is lost by the limiter, the middleware emits a success response for a file that should have been rejected for exceeding limits.fileSize. The practical footprint is narrowed further: only applications combining both async fileFilter and a fileSize limit trigger the race window.
The vendor's LOW / 3.7 score is accurate and honest. The CVSS vector (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N) correctly captures: network-reachable but high complexity, no auth required, no confidentiality or availability impact, and only low integrity impact. Critically, the advisory itself notes that the underlying busboy / multipart parser still truncates the stream at the configured size limit, so even a 'successful' bypass doesn't deliver a full oversized file to disk or memory — the data is cut off. This makes the real-world impact even lower than the CVSS suggests: the attacker bypasses the *rejection message*, not the actual size enforcement.
4 steps from start to impact.
Identify a multer-powered upload endpoint
multipart/form-data uploads. Because multer serves ~20 million npm weekly downloads, these are extremely common in Node.js web apps. The attacker confirms the endpoint enforces a file-size limit (e.g., via 413 Payload Too Large or a custom error).- Target runs a Node.js app with multer < 2.3.0
- Endpoint accepts file uploads
- Many apps use synchronous fileFilter or no fileFilter at all, which is not vulnerable
Confirm async fileFilter + fileSize limit
fileFilter callback alongside limits.fileSize. The attacker has no reliable way to fingerprint this server-side configuration remotely — it requires source code review or blind fuzzing with timing analysis. This dramatically reduces practical exploitability.- Application uses async fileFilter callback
- Application sets limits.fileSize
- No remote fingerprinting method exists for this config combo
- Many production apps use sync fileFilter or omit it entirely
Race the async filter against the size limiter
limits.fileSize and attempts to win a timing race so the async filter resolves *after* the limiter has started but *before* it can reject the request. The race window depends on event-loop scheduling and the duration of the async filter (e.g., a database lookup). Success is probabilistic, not deterministic.- Async filter takes enough time to open a race window
- Attacker can send repeated uploads to increase odds
- Race conditions in the Node.js single-threaded event loop have narrow, non-deterministic windows
- Rate limiting, WAF, or upload throttling reduces retry capacity
File accepted — but still truncated
busboy) still truncates the stream at the configured byte limit. The file written to disk or buffer is the same size it would have been anyway — the attacker merely avoided the rejection response. No oversized payload actually lands.- Race condition successfully triggered
- Parser-level truncation is a hard backstop the attacker cannot bypass through this CVE
- The 'bypass' delivers no additional data to the server
The supporting signals.
| In-the-wild exploitation | None observed. Not listed on CISA KEV. No campaign reports from any threat-intel vendor as of 2026-08-29. |
|---|---|
| Proof of concept | None public. No PoC repos found on GitHub or exploit databases. The race condition is non-trivial to reproduce reliably. |
| EPSS | Not yet scored (CVE published 2026-08-28, EPSS typically populates within 24-48 hours). Expected to be very low given LOW severity and no PoC. |
| KEV status | Not listed. No CISA KEV entry. |
| CVSS vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N — Network vector but High complexity. Integrity-only impact at Low level. No confidentiality or availability impact. |
| Affected versions | multer < 2.3.0 (all prior versions when using async fileFilter + fileSize limit) |
| Fixed version | multer 2.3.0 (GitHub advisory GHSA-qvfw-j98x-7q72) |
| Install base | ~20 million weekly npm downloads. Key ecosystem package for Node.js file uploads. However, only the subset using async fileFilter + fileSize limits is affected. |
| Disclosure date | 2026-08-28 (reserved 2026-08-20). Published via OpenJS Foundation CNA. |
| Reporter | ThinkerHao (reporter); bjohansebas (fix author); UlisesGascon (fix reviewer) |
noisgate verdict.
The single most decisive factor is the parser-level truncation backstop: even when the race condition succeeds, the underlying busboy parser still enforces the byte limit, so no oversized data actually reaches the application — the attacker bypasses only the rejection message, not the size enforcement itself. This reduces the real-world integrity impact to near-zero, fully justifying the vendor's LOW rating.
Why this verdict
- Parser backstop nullifies impact: Even a successful race only bypasses the rejection *response*, not the actual byte truncation. The file on disk is the same size either way — the attacker gains nothing tangible.
- High attack complexity with no reliable trigger: The race depends on Node.js event-loop timing and the duration of the async fileFilter callback. It is probabilistic, not weaponizable at scale.
- Narrow configuration surface: Only apps combining async fileFilter AND fileSize limits are affected. Many multer deployments use synchronous filters or no filter at all, shrinking the vulnerable population well below the 20M-download install base.
- Role multiplier: multer is an application-layer npm middleware, not infrastructure software. (a) *Low-value role:* dev servers, internal tools — negligible impact. (b) *Typical role:* line-of-business Node.js APIs — impact is a cosmetic bypass of an error message, no data exposure. (c) *High-value role:* a Node.js API fronting regulated data — even here, the parser truncation means no oversized payload lands. The blast radius is host-local, cosmetic only across all roles. No role elevates this to domain, fleet, or supply-chain impact.
Why not higher?
The vulnerability has zero confidentiality and zero availability impact per the CVSS vector. The integrity impact is further neutered by the parser-level truncation backstop — the attacker cannot deliver oversized content. There is no path from this bug to RCE, privilege escalation, or data exfiltration in any deployment role.
Why not lower?
While the practical impact is near-zero, the bug does represent a real logic flaw in a widely-used package where the middleware's own enforcement fails. An application relying solely on multer's rejection callback for business logic (e.g., logging rejected uploads, user-facing error messages) would behave incorrectly. This is a legitimate, if minor, integrity issue — not ignorable.
What to do — in priority order.
- Add application-level file-size validation after upload — Check
req.file.sizeorBuffer.byteLengthin your route handler *after* multer processes the upload, and reject files exceeding your policy. This is a defense-in-depth layer that doesn't depend on multer's limit enforcement. No mitigation SLA applies at LOW severity — treat as backlog hygiene. - Switch to synchronous fileFilter if async logic isn't needed — If your fileFilter doesn't perform I/O (no DB lookups, no async checks), rewrite it as a synchronous callback. Synchronous filters are not affected by this race condition.
- Upgrade to multer 2.3.0 — The definitive fix. Run
npm install [email protected]or update yourpackage-lock.json. Test file-upload endpoints in staging before production rollout.
- WAF file-size limits — A WAF enforcing
Content-Lengthor request body size is a useful layer but doesn't address the race condition in multer's internal logic. It may also not catchTransfer-Encoding: chunkeduploads whereContent-Lengthis absent. - Rate limiting alone — Throttling upload attempts reduces race-condition retry odds but doesn't fix the underlying bug. With a sufficiently slow async filter, even a single request can trigger the race.
Crowdsourced verification payload.
Run this on any machine with node and npm installed. It checks the installed multer version in the current project directory. No special privileges needed. Example: bash check_multer_cve_2026_77063.sh /path/to/your/node/project
#!/usr/bin/env bash
# check_multer_cve_2026_77063.sh
# Checks whether the installed multer version is vulnerable to CVE-2026-77063
# Usage: bash check_multer_cve_2026_77063.sh [/path/to/project]
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-.}"
if [ ! -d "$PROJECT_DIR/node_modules/multer" ]; then
echo "UNKNOWN — multer is not installed in $PROJECT_DIR/node_modules"
exit 2
fi
VERSION=$(node -e "try { console.log(require('$PROJECT_DIR/node_modules/multer/package.json').version) } catch(e) { console.log('error') }")
if [ "$VERSION" = "error" ]; then
echo "UNKNOWN — could not read multer version from package.json"
exit 2
fi
# Compare versions: vulnerable if < 2.3.0
VULN=$(node -e "
const semver = '$VERSION'.split('.').map(Number);
const fixed = [2, 3, 0];
for (let i = 0; i < 3; i++) {
if (semver[i] < fixed[i]) { console.log('yes'); process.exit(0); }
if (semver[i] > fixed[i]) { console.log('no'); process.exit(0); }
}
console.log('no');
")
if [ "$VULN" = "yes" ]; then
echo "VULNERABLE — multer $VERSION is affected by CVE-2026-77063 (fixed in 2.3.0)"
exit 1
else
echo "PATCHED — multer $VERSION is >= 2.3.0"
exit 0
fiIf you remember one thing.
npm install — roll it into your next scheduled dependency update cycle. If you run npm audit in CI, this will flag automatically. Prioritize your team's time on higher-severity items; this one can ride the next sprint's dependency bump.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.