A carpenter's workshop radio that crashes if a stranger shouts the wrong word at it — except the workshop was never open to strangers
webpack-dev-server is the *local development* HTTP server bundled with the webpack toolchain. It listens on localhost:8080 (or a developer-chosen port) while a frontend engineer is actively editing code, serving hot-reloaded JS/CSS bundles to their browser. CVE-2026-14631 describes a CWE-20 improper input validation flaw in the Host/Origin header parsing path: a malformed header value triggers an unhandled exception inside the dev-server's request middleware, terminating the Node.js process. Affected versions per the advisory chain cover webpack-dev-server 4.x prior to the patched 4.15.2 line and 5.x prior to 5.2.1; the fix is a defensive try/catch around the header normalization routine.
The vendor MEDIUM (5.3) rating uses AV:N/AC:L/PR:N/UI:N/A:L — network-reachable, unauth, low complexity, availability-only. Mechanically the vector is correct. Contextually it is overstated. webpack-dev-server is explicitly documented as a *development-time* tool; the maintainers have said for years that it is not hardened for production or hostile-network exposure. The realistic impact is: an attacker on the same LAN as a developer crashes the developer's hot-reload server, forcing an npm run dev restart. There is no confidentiality loss, no integrity loss, no lateral movement, and no persistence. This is a nuisance, not a security event.
3 steps from start to impact.
Reach the dev-server socket
localhost in webpack-dev-server 4.x+, which means the attacker must already be executing code on the developer's workstation OR the developer explicitly reconfigured host: '0.0.0.0' to expose it on the LAN (a common but discouraged pattern for mobile-device testing).- dev-server bound to a non-loopback interface
- attacker on same L2/L3 segment as developer OR local code execution
- default bind is 127.0.0.1 since v4
- corporate laptops behind host firewall block inbound 8080
- no public exposure — dev servers are not published to the internet
webpack-dev-server banner return a trivial global footprint (<5k hosts), overwhelmingly home labs and misconfigured CI runnersSend malformed Host/Origin header
curl -H 'Host: \x00\x00' style, or a header containing characters that fail the URL parser) against the dev-server endpoint. The malformed value hits the input-validation gap in the header-check middleware and raises an uncaught exception.- ability to write arbitrary bytes into HTTP request headers
- any reverse proxy in front (nginx, Traefik) normalizes or rejects malformed headers before they reach Node
curl; no CVE-specific IDS signature exists, but generic malformed-HTTP alerts fireNode process terminates
npm start (seconds to minutes).- no process supervisor auto-restarting the dev-server
nodemon,pm2, or IDE-integrated task runners restart the process automatically- developer notices immediately because the browser tab disconnects
host: 'localhost' bind — The webpack-dev-server default already blocks this attack path for LAN-based attackers. Audit webpack.config.js / devServer blocks in your monorepo and remove any explicit host: '0.0.0.0' overrides unless there is a documented mobile-testing need. No mitigation SLA — this is standing hygiene, not a deadline-driven action.proxy_set_header Host normalization. Malformed headers are stripped before they reach the Node process.nodemon, pm2, or your IDE task runner will restart the process on crash, reducing the nuisance window to seconds. Most developers already do this without realizing it.4.15.2 / 5.2.1 on your next Renovate/Dependabot sweep. Apply within the noisgate remediation SLA of 365 days — no need to jump the queue.- WAF rules on production perimeter — dev-server never lives behind your prod WAF
- EDR on the developer workstation — this is a benign Node crash, not a malware event; no EDR rule will fire
- Network segmentation — irrelevant when the default bind is already loopback
- MFA / auth hardening — the vuln is pre-auth by design because there is no auth on dev-server
The supporting signals.
| In-the-wild exploitation | None observed. No campaigns, no honeypot hits, no incident-response reports referencing this CVE |
|---|---|
| Public PoC | Trivial one-liner (curl with a malformed header); no dedicated exploit repo warrants naming |
| EPSS | Expected <0.10% / <5th percentile — consistent with dev-tool DoS CVEs in the same class |
| KEV status | Not listed. Extremely unlikely to be added — CISA KEV criteria require active exploitation with meaningful impact |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L — availability-only, scope unchanged, no confidentiality or integrity impact |
| Affected versions | webpack-dev-server <4.15.2 and 5.0.0 – 5.2.0 |
| Fixed versions | 4.15.2, 5.2.1 — defensive header-parse wrapping |
| Exposure telemetry | Shodan/Censys facet for webpack-dev-server banners: negligible internet-facing population; this tool is not meant to be public |
| Disclosure | Reported via GitHub Security Advisory workflow to the webpack maintainers; no named researcher of note |
| Deployment context | Developer workstation only. Any production build pipeline that ships webpack-dev-server to prod is a much larger process failure than this CVE |
Sources.
- webpack-dev-server GHSA index
- GHSA-9jgg-88mc-972h — prior wds Host/Origin advisory (context)
- GHSA-79cf-xcqc-c78w — related cross-origin advisory
- webpack-dev-server issue #1604 — Invalid Host/Origin warning
- webpack-dev-server issue #3997 — HTTPS disconnect on bad header
- Snyk package page for webpack-dev-server
- OSV entry for related wds advisory
- IBM security bulletin referencing wds CVEs
Why this verdict
- Vendor baseline is generous: MEDIUM (5.3) treats a network-reachable DoS as inherently meaningful, but the network in question is a developer's loopback interface by default.
- Reachability floor is near-zero: default bind is
127.0.0.1since webpack-dev-server 4.x, and no sane deployment exposes the dev port to the internet — Shodan population is negligible. - Impact ceiling is trivial: availability-only, single Node process, auto-restarted by
nodemon/pm2/IDE task runners in the vast majority of dev environments. - Role multiplier — dev workstation: chain succeeds → developer's hot-reload restarts. Blast radius = one workstation, one process, one restart. No fleet, identity, or supply-chain exposure.
- Role multiplier — CI runner running
webpack-dev-server(rare, misconfigured): chain succeeds → CI job fails and retries. Still no code-execution, no artifact-tampering vector — this is a header-parse crash, not an RCE or code-injection primitive. Floor stays LOW. - Role multiplier — production (misconfigured, actively discouraged by maintainers): even here impact is a single-tenant process restart, not a supply-chain pivot; the CVE does not enable code execution or data egress.
Why not higher?
MEDIUM would require a plausible operational-impact story — this has none. There is no path to code execution, credential theft, or data disclosure, and the tool is not deployed in high-value roles. KEV listing is implausible.
Why not lower?
Not IGNORE because a fix exists, the CVE is real, and there is a non-zero nuisance impact on developers who bind the dev-server to 0.0.0.0 for device testing. It belongs on the backlog to be swept up by the next dependency-freshness cycle.
Crowdsourced verification payload.
Run on any Linux/macOS developer workstation or CI runner that contains a package-lock.json / yarn.lock / pnpm-lock.yaml. Invoke as ./check-wds.sh /path/to/repo (defaults to the current directory). No elevated privileges required — this is a lockfile grep, not a network probe.
#!/usr/bin/env bash
# noisgate: webpack-dev-server CVE-2026-14631 lockfile check
# Usage: ./check-wds.sh [repo-root]
set -u
ROOT="${1:-.}"
cd "$ROOT" 2>/dev/null || { echo "UNKNOWN: cannot cd to $ROOT"; exit 2; }
# Find every lockfile in the tree (monorepo-safe)
mapfile -t LOCKS < <(find . -type f \( -name 'package-lock.json' -o -name 'yarn.lock' -o -name 'pnpm-lock.yaml' \) -not -path '*/node_modules/*')
if [ "${#LOCKS[@]}" -eq 0 ]; then
echo "UNKNOWN: no lockfiles found under $ROOT"
exit 2
fi
vuln=0
found=0
for lf in "${LOCKS[@]}"; do
# Extract every webpack-dev-server version pinned in the lockfile
vers=$(grep -E 'webpack-dev-server' "$lf" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' | sort -u)
for v in $vers; do
found=1
IFS='.' read -r MA MI PA <<<"$v"
# Vulnerable ranges: <4.15.2 OR 5.0.0-5.2.0
if [ "$MA" -lt 4 ]; then
echo "VULNERABLE: $lf pins webpack-dev-server $v (<4.15.2)"; vuln=1
elif [ "$MA" -eq 4 ] && { [ "$MI" -lt 15 ] || { [ "$MI" -eq 15 ] && [ "$PA" -lt 2 ]; }; }; then
echo "VULNERABLE: $lf pins webpack-dev-server $v (<4.15.2)"; vuln=1
elif [ "$MA" -eq 5 ] && { [ "$MI" -lt 2 ] || { [ "$MI" -eq 2 ] && [ "$PA" -lt 1 ]; }; }; then
echo "VULNERABLE: $lf pins webpack-dev-server $v (<5.2.1)"; vuln=1
else
echo "PATCHED: $lf pins webpack-dev-server $v"
fi
done
done
if [ "$found" -eq 0 ]; then echo "PATCHED: webpack-dev-server not present"; exit 0; fi
exit $vuln