← Back to Feed CACHED · 2026-07-09 12:03:30 · CACHE_KEY CVE-2026-12590
CVE-2026-12590 · CWE-770 · Disclosed 2026-07-09

Impact: In body-parser versions prior to 1

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

A speed limiter that only fails when you set the speed limit to 'banana'

body-parser is the Express middleware that parses incoming request bodies (JSON, urlencoded, raw, text). In versions prior to 1.20.6 (1.x) and prior to 2.3.0 (2.x), if the application passes an *invalid* value to the limit option (something bytes cannot parse — e.g. 'infinity', '10 potatoes', negative numbers), the parser silently falls through to no effective cap. That lets a client stream an arbitrarily large body into memory, driving RSS growth until the Node worker crashes or the pod OOM-kills. The trigger is not a malicious request pattern in isolation — it requires the app to have shipped with a bad config.

Vendor severity of LOW / 3.7 is honest. CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L captures the reality: attack complexity is High because it depends on a misconfiguration the maintainer already committed, and the impact ceiling is a single-process availability hit. No confidentiality, no integrity, no code execution. Reassessment holds it at LOW — this is a code-hygiene bug, not an incident.

"Node.js body-parser DoS that only fires when a developer already misconfigured the limit option — self-inflicted, low blast radius."
02 · The Attack Path

3 steps from start to impact.

STEP 01

Ship a misconfigured limit

A developer passes an unparseable limit string to bodyParser.json({ limit: 'infinity' }) or similar. The bytes library returns null, and body-parser fails to substitute the default '100kb' cap. This is the *pre-condition* — no attacker action yet.
Conditions required:
  • App uses body-parser 1.x <1.20.6 or 2.x <2.3.0
  • Developer passes a non-numeric / unparseable limit value
Where this breaks in practice:
  • Most apps use the default limit or a valid string like '1mb'
  • Linters and code review catch obviously wrong values
  • Framework scaffolds (Express Generator, NestJS) ship sane defaults
Detection/coverage: SAST tools (Semgrep, CodeQL) can flag suspicious limit values; npm audit flags the CVE once the advisory is indexed
STEP 02

Reach the vulnerable endpoint

Attacker identifies an HTTP endpoint that accepts a body — typically a POST/PUT with JSON. Uses a simple client (curl, hey, or a custom script sending Content-Length: 5000000000) and streams garbage.
Conditions required:
  • Endpoint reachable from attacker's network position
  • Endpoint uses the misconfigured parser instance
Where this breaks in practice:
  • Upstream reverse proxies (nginx, ALB, Cloudflare) enforce their own body-size limits well below Node's memory ceiling
  • WAFs cap request size independently
  • Rate limiters and connection limits blunt sustained streaming
Detection/coverage: Access logs show anomalous Content-Length; APM (Datadog, New Relic) surfaces memory spikes
STEP 03

Exhaust worker memory

Attacker sends one or a few oversized requests. Node buffers the body in memory until the process hits its heap limit or the container OOMs. On single-worker deployments this drops the service; on clustered deployments it recycles one worker at a time.
Conditions required:
  • Node process has enough headroom for buffer to be observable
  • No upstream size cap intervenes first
Where this breaks in practice:
  • Kubernetes restart policies recover within seconds
  • PM2 / cluster mode absorbs single-worker crashes
  • Horizontal autoscaling masks the impact
Detection/coverage: OOMKilled events in kubelet logs; process restart counters in PM2/systemd
03 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNone observed. No KEV listing, no campaign reporting, no honeypot hits attributable to this CVE.
Proof-of-conceptReproducer is trivial (three lines of Express + curl --data-binary @big.bin); no weaponized public PoC repo of note as of disclosure.
EPSSFresh CVE (disclosed 2026-07-09); EPSS not yet scored. Class-comparable DoS-via-misconfig bugs typically land <0.5% percentile.
KEV statusNot listed. Unlikely candidate — no code execution, requires pre-existing misconfig.
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L — Network, High complexity, no auth, Low availability impact only.
Affected versionsbody-parser < 1.20.6 (1.x line) and body-parser < 2.3.0 (2.x line). Also transitively affects Express <4.x bundling old body-parser.
Fixed versions1.20.6 and 2.3.0 — patch validates the limit option at parser-instantiation time and falls back to the default when unparseable.
Exposure populationbody-parser has ~50M weekly npm downloads; realistic vulnerable-config population is a small fraction — most consumers pass no limit or a valid string.
Disclosure2026-07-09 via the body-parser GitHub Security Advisory.
ReporterCredited in the GHSA / npm advisory (see sources).
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to LOW (3.5/10)

The decisive factor is that exploitation is gated on the application having *already* shipped an invalid limit value — a self-inflicted misconfiguration, not an attacker-controlled precondition — and the ceiling is single-process availability with no confidentiality or integrity impact. Upstream proxies and container restart policies further cap real-world impact to transient service blips.

HIGH Severity bucket (LOW)
HIGH Affected version ranges
MEDIUM Real-world vulnerable-config prevalence

Why this verdict

  • Precondition is self-inflicted: the vuln requires the developer to have already passed an invalid limit — a bug the attacker cannot induce remotely. That's the AC:H in the vector, and it's earned.
  • Impact ceiling is A:L only: worst case is a Node worker OOM. No RCE, no info disclosure, no persistence. Modern orchestration (K8s, PM2, systemd) recycles the process in seconds.
  • Upstream body-size caps neutralize most exposure: nginx client_max_body_size, ALB/Cloudflare request limits, and WAF policies typically enforce a ceiling far below what would starve a Node worker.
  • Role multiplier — line-of-business web tier: even on a public-facing API server, the outcome is transient DoS of one process. Not fleet-scale, not identity-scale, not supply-chain-scale. Floor stays LOW.
  • Role multiplier — high-value roles (auth service, payment gateway): same outcome, transient DoS. No pivot to identity or data. LOW floor holds.
  • No exploitation signal: not in KEV, no campaigns, PoC is trivial but uninteresting to weaponize because it demands the target already be broken.

Why not higher?

MEDIUM would imply the vuln is reachable across a meaningful population of default-configured deployments. It isn't — a correctly-configured or default-configured body-parser is *not* vulnerable. The attacker cannot induce the trigger; they can only exploit an existing mistake, which is what AC:H captures.

Why not lower?

IGNORE would be wrong because the bug is real, the fix is trivial, and misconfigured deployments do exist in the wild (especially older Express apps with hand-rolled config). Patching remains worthwhile as hygiene and to close a class-C availability hazard against public endpoints.

05 · Compensating Control

What to do — in priority order.

  1. Audit all bodyParser.*({ limit: ... }) call sites — Grep the codebase for limit: inside body-parser, express.json, express.urlencoded, express.raw, express.text calls. Confirm every value is a valid bytes-parseable string (e.g. '100kb', '1mb') or an integer. No mitigation SLA applies for LOW — treat as backlog hygiene, but close within the 365-day remediation window.
  2. Enforce an upstream body-size cap — Set client_max_body_size 1m; in nginx, client_body_buffer_size bounds in HAProxy, or the equivalent on your ALB / Cloudflare / API gateway. This makes the Node process unreachable at oversized-body volumes regardless of application config. Deploy as standard baseline within backlog hygiene cadence.
  3. Bump body-parser to 1.20.6 or 2.3.0 — Update package.json and run npm audit fix or pnpm up body-parser. If Express is a transitive consumer, resolve via overrides (npm) or resolutions (yarn/pnpm). Ship in the next routine dependency-update cycle.
  4. Add a Semgrep/CodeQL rule for invalid limit values — Codify a lint rule that rejects non-string / non-integer literals in limit: and forbids values that don't match ^\d+(\.\d+)?\s*(b|kb|mb|gb)$/i. Prevents regressions across the fleet.
What doesn't work
  • WAF signatures on request bodies — the payload isn't malicious content, it's just volume. WAFs that inspect body content will happily let a stream of zeros through unless a size cap is also configured.
  • Rate limiting by request count — one large request can kill a worker; a per-IP RPS limiter that allows even 1 request/sec is enough to sustain the DoS.
  • Container memory limits alone — they contain the blast radius but do not prevent the DoS; the pod still cycles.
06 · Verification

Crowdsourced verification payload.

Run on any host or CI runner with Node.js and jq installed, from your repo root. Invoke as ./check-cve-2026-12590.sh /path/to/project (defaults to .). No elevated privileges required — this is a read-only manifest and lockfile inspection.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check-cve-2026-12590.sh
# Detect vulnerable body-parser versions and suspicious limit configurations.
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -u
PROJECT_DIR="${1:-.}"
cd "$PROJECT_DIR" 2>/dev/null || { echo "UNKNOWN: cannot cd to $PROJECT_DIR"; exit 2; }

STATUS=0
FOUND_ANY=0

vercmp_lt() {
  # returns 0 if $1 < $2 (semver-ish)
  [ "$1" = "$2" ] && return 1
  printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1 | grep -qx "$1"
}

check_version() {
  local v="$1"
  FOUND_ANY=1
  local major="${v%%.*}"
  if [ "$major" = "1" ]; then
    if vercmp_lt "$v" "1.20.6"; then
      echo "VULNERABLE: body-parser $v (1.x < 1.20.6)"
      STATUS=1
    else
      echo "PATCHED: body-parser $v"
    fi
  elif [ "$major" = "2" ]; then
    if vercmp_lt "$v" "2.3.0"; then
      echo "VULNERABLE: body-parser $v (2.x < 2.3.0)"
      STATUS=1
    else
      echo "PATCHED: body-parser $v"
    fi
  else
    echo "UNKNOWN: unexpected major version $v"
    [ $STATUS -eq 0 ] && STATUS=2
  fi
}

# 1. Inspect installed tree via npm ls (authoritative)
if command -v npm >/dev/null 2>&1 && [ -f package.json ]; then
  while IFS= read -r ver; do
    [ -n "$ver" ] && check_version "$ver"
  done < <(npm ls body-parser --all --parseable --long 2>/dev/null \
            | awk -F: '/body-parser@/ {sub(/.*body-parser@/,""); print $1}' \
            | sort -u)
fi

# 2. Fallback: scan lockfiles
if [ $FOUND_ANY -eq 0 ]; then
  for lf in package-lock.json npm-shrinkwrap.json; do
    [ -f "$lf" ] || continue
    if command -v jq >/dev/null 2>&1; then
      while IFS= read -r ver; do
        [ -n "$ver" ] && check_version "$ver"
      done < <(jq -r '.. | objects | select(.version and (input_filename | test("body-parser") | not)) | select(.resolved? // "" | test("body-parser")) | .version' "$lf" 2>/dev/null | sort -u)
    fi
  done
fi

# 3. Config heuristic: flag suspicious limit values
echo "--- scanning source for suspicious limit configs ---"
SUSPECT=$(grep -RInE "limit\s*:\s*['\"]?(infinity|Infinity|-[0-9]+|0)" \
            --include='*.js' --include='*.ts' --include='*.mjs' --include='*.cjs' \
            . 2>/dev/null)
if [ -n "$SUSPECT" ]; then
  echo "VULNERABLE-CONFIG: suspicious limit values detected:"
  echo "$SUSPECT"
  STATUS=1
fi

if [ $FOUND_ANY -eq 0 ] && [ -z "$SUSPECT" ]; then
  echo "UNKNOWN: body-parser not found in project"
  exit 2
fi

if [ $STATUS -eq 0 ]; then
  echo "RESULT: PATCHED"
elif [ $STATUS -eq 1 ]; then
  echo "RESULT: VULNERABLE"
else
  echo "RESULT: UNKNOWN"
fi
exit $STATUS
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: don't panic-page anyone. This is a LOW-severity availability bug in body-parser that only detonates on apps that already shipped a broken limit: value. Per the noisgate mitigation SLA for LOW, there is no mitigation SLA — go straight to the noisgate remediation SLA window of ≤365 days. Fold the version bump (1.20.6 / 2.3.0) into your next routine dependency-update cycle, run the verification script across your Node monorepos to flag the ~1-in-a-thousand codebase that passes 'infinity' or a negative number, and confirm your edge proxies enforce a sane client_max_body_size. If any of your public-facing Node services *do* trip the config heuristic, close those specific instances within 30 days as targeted hygiene — the class-wide fleet has more urgent work.

Sources

  1. GitHub Advisory Database — body-parser
  2. body-parser repository (expressjs/body-parser)
  3. body-parser CHANGELOG
  4. npm — body-parser package
  5. bytes library (limit parser)
  6. CWE-770: Allocation of Resources Without Limits or Throttling
  7. NVD CVE search
  8. CISA KEV catalog
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.