A prank doorbell wired to a door that only opens when you're already sitting on the couch
webpack-dev-server ≤ 5.2.5 ships two internal endpoints — /webpack-dev-server/open-editor and the sibling open-in-editor route — with no CSRF token, no Origin/Referer check, and permissive CORS behavior on the developer's loopback listener. When a developer running npm run start visits an attacker-controlled page, that page can issue a cross-origin request that causes the dev server to spawn the developer's configured editor (code, vim, webstorm, etc.) against a file path the attacker chooses. The result is *editor-open thrashing*, potential UI hijack, and in edge cases where the editor CLI parses -- arguments unsafely, further mischief on the developer's own workstation.
Vendor rated this MEDIUM (4.7) with AV:N/UI:R/S:C/C:N/I:N/A:L. The AV:N is technically accurate (the trigger is a network-loaded HTML page) but misleading — the *target* is always localhost on a developer's machine while webpack-dev-server is actively running. The S:C and A:L reflect that the impact escapes the browser sandbox into the OS, but only to the extent of launching an editor. Vendor score is defensible; if anything it slightly overweights the scope change. This is a *developer-hygiene* bug, not a production server bug — webpack-dev-server should never be reachable outside a laptop.
4 steps from start to impact.
Developer starts webpack-dev-server locally
npm run start / webpack serve which binds the vulnerable dev server, typically on 127.0.0.1:8080 or 0.0.0.0:8080. The /webpack-dev-server/open-editor route is registered by default in versions ≤ 5.2.5.- Developer is actively running webpack-dev-server
- Dev server bound to a port the browser can reach
- Server is short-lived — only runs during active development
- Not present in CI, staging, or production builds
npm audit) will flag versions ≤ 5.2.5. No runtime IDS signature is meaningful here.Developer visits malicious page in same browser session
http://localhost:8080, opens an attacker-controlled URL (phishing link, malvertising, compromised npm doc site, Stack Overflow answer with embedded iframe). The attacker page runs JavaScript that fetches or embeds http://localhost:8080/webpack-dev-server/open-editor?fileName=....- Same browser profile has dev-server tab and attacker tab
- No browser-level localhost-blocking extension
- Chrome's *Private Network Access* preflight blocks cross-origin requests from public → private IPs by default in current Chromium builds — attacker page must satisfy PNA
- Firefox and Safari have similar loopback restrictions in progress
- Corporate DNS/proxy that force all traffic through a gateway makes loopback-from-web awkward
CSRF fires — editor launches with attacker-supplied path
code --goto <path>) with an attacker-chosen fileName. Best case: editor pops up on a random file. Worst plausible case: editor CLI argument injection or opening a UNC/file:// path that triggers editor-side behavior.- Developer has an editor launcher configured
- Editor CLI does not sanitize its own arguments
- Most editors (VS Code, JetBrains) treat
--gotoarguments as opaque paths and refuse shell metacharacters - Editor launch is loud — the developer sees the window pop
node → code.exe) but will not flag it as malicious.Impact — nuisance, DoS, or narrow local pivot
- Attacker page stays open long enough to iterate
- Developer notices immediately and kills the process
- Impact terminates when dev-server is stopped
The supporting signals.
| In-the-wild exploitation | None observed. No KEV entry, no known campaigns, no honeypot hits (nothing to hit — target is loopback). |
|---|---|
| Public PoC | Conceptual PoC in the disclosure advisory (single HTML page with fetch('http://localhost:8080/webpack-dev-server/open-editor?...')). No weaponized tooling in Metasploit/Nuclei. |
| EPSS | Not yet scored at disclosure +1 day; expected to sit <0.1% (bottom quartile) given loopback-only target. |
| KEV status | Not listed. Would be highly unusual for a dev-only loopback CSRF to appear on KEV. |
| CVSS interpretation | CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:N/A:L → 4.7 MEDIUM. Realistic environmental score with MAV:L (target is loopback) drops to ~3.1 LOW. |
| Affected versions | webpack-dev-server ≤ 5.2.5 across the 3.x, 4.x, and 5.x lines. |
| Fixed version | 5.2.6 (upstream). Distro backports are irrelevant — this is an npm-only package. |
| Exposure telemetry | Shodan/Censys show a trickle of webpack-dev-server banners on public IPs (misconfigured host: '0.0.0.0'), historically ~200–500 hosts globally — a rounding error relative to npm install counts (~15M/week). |
| Disclosure | 2026-07-03 via the webpack GitHub Security Advisory pipeline. |
| Reporter | Credited to the webpack maintainer team via community security disclosure; no named external researcher in the advisory. |
noisgate verdict.
The decisive factor is target locality: webpack-dev-server is a developer-workstation tool bound to loopback; the vulnerable endpoint is unreachable from any production surface and the realistic blast radius is a single developer's editor window. Vendor's MEDIUM assumes the AV:N vector matters uniformly, but browser Private Network Access controls and the developer-only lifetime of the process collapse the exploit population to negligible.
Why this verdict
- Loopback-only target: the vulnerable endpoint lives on the developer's
127.0.0.1dev server. Deducting ~2 points off the vendor baseline forMAV:L. - UI:R + tight session window: exploitation requires the developer to visit a hostile page *while*
webpack serveis running. Dev servers run for hours, not months — narrow window. - Browser PNA already mitigates: Chromium's Private Network Access preflight blocks the exact cross-origin → loopback fetch pattern this CSRF depends on, catching the majority of drive-by attempts before they reach step 3.
- Impact ceiling is A:L only: vendor's own vector says no confidentiality or integrity loss. No credential theft, no code execution on build artifacts, no lateral movement.
- Role multiplier — dev workstation: the affected component is *by definition* a developer tool. It never runs on domain controllers, hypervisors, IdPs, CAs, PAM, backup, CI runners (CI uses
webpack build, notwebpack serve), or edge appliances. The high-value-role catalog does not apply. Floor is LOW. - Role multiplier — misconfigured
host: '0.0.0.0'on shared dev boxes: in rare cases teams bind dev servers to all interfaces on a shared VM. Even then, the chain still ends at editor-open on that box — no fleet or identity impact. Does not raise the floor.
Why not higher?
MEDIUM would require the endpoint to be reachable from a production surface or to affect a role where compromise cascades (CI runner, container registry, IdP). It doesn't — webpack-dev-server is developer-only and the impact ceiling is editor spam. HIGH/CRITICAL are entirely off the table absent a chained editor-CLI RCE, which is a separate CVE against the editor.
Why not lower?
IGNORE is wrong because the fix is trivial (bump to 5.2.6) and organizations with strict SBOM policies will fail builds until the version is bumped. Leaving the vulnerable version in package-lock.json invites drive-by nuisance in dev environments and audit noise. Treat as backlog hygiene, not zero.
What to do — in priority order.
- Bump
webpack-dev-serverto ≥ 5.2.6 inpackage.json/ lockfiles — This is the actual fix — no mitigation SLA applies at LOW severity, so fold it into the standard 365-day dependency-hygiene cycle (next quarterly dep-refresh sprint). Usenpm audit fixor Dependabot/Renovate auto-PRs. - Enforce
host: '127.0.0.1'inwebpack.config.jsdevServerblock — Prevents the dev server from binding to0.0.0.0even by accident, so LAN-based attackers on the same coffee-shop Wi-Fi cannot reach it. Add a lint rule or a shared config preset. - Ensure Chromium-based browsers on developer laptops have Private Network Access enforcement enabled — PNA blocks the cross-origin → loopback preflight this CSRF depends on. Enforce via
chrome://policy(InsecurePrivateNetworkRequestsAllowed=false) through your MDM. - Educate developers to run dev servers in a browser profile separate from general browsing — A
--user-data-dirprofile dedicated tolocalhost:8080eliminates the cross-tab CSRF path entirely. Ship as a documented workstation setup step.
- WAF / reverse-proxy rules — the dev server sits on loopback behind no proxy; there is no WAF in the path.
- EDR alerting on
node.exechild processes —nodelegitimately spawns editors and shells constantly during development; you will drown in false positives. - Network segmentation / firewall rules — the traffic is browser → 127.0.0.1, never crossing the NIC. Firewall cannot see it.
- Blocking outbound to attacker C2 — irrelevant. The exploit runs entirely in-browser and locally; no C2 callback required.
Crowdsourced verification payload.
Run on any developer workstation or CI SBOM job that has Node.js installed. Invoke from the repo root: bash check-wds.sh /path/to/repo (defaults to current directory). No elevated privileges required — read-only inspection of package-lock.json / yarn.lock / pnpm-lock.yaml.
#!/usr/bin/env bash
# noisgate verification: CVE-2026-14620 — webpack-dev-server CSRF on /open-editor
# Vulnerable: <= 5.2.5 Fixed: >= 5.2.6
# Exit codes: 0=PATCHED 1=VULNERABLE 2=UNKNOWN
set -u
REPO="${1:-.}"
cd "$REPO" 2>/dev/null || { echo "UNKNOWN: cannot cd to $REPO"; exit 2; }
FIXED_MAJOR=5
FIXED_MINOR=2
FIXED_PATCH=6
vercmp() { # returns 0 if $1 < $2
IFS=. read -r a1 a2 a3 <<< "$1"
IFS=. read -r b1 b2 b3 <<< "$2"
[ "${a1:-0}" -lt "${b1:-0}" ] && return 0
[ "${a1:-0}" -gt "${b1:-0}" ] && return 1
[ "${a2:-0}" -lt "${b2:-0}" ] && return 0
[ "${a2:-0}" -gt "${b2:-0}" ] && return 1
[ "${a3:-0}" -lt "${b3:-0}" ] && return 0
return 1
}
found=0
vuln=0
# npm lockfile v2/v3
if [ -f package-lock.json ]; then
found=1
versions=$(grep -oE '"node_modules/webpack-dev-server":[[:space:]]*\{[^}]*"version":[[:space:]]*"[0-9.]+"' package-lock.json \
| grep -oE '"version":[[:space:]]*"[0-9.]+"' | grep -oE '[0-9.]+')
for v in $versions; do
if vercmp "$v" "$FIXED_MAJOR.$FIXED_MINOR.$FIXED_PATCH"; then
echo "VULNERABLE: package-lock.json pins webpack-dev-server $v (< 5.2.6)"
vuln=1
fi
done
fi
# yarn.lock
if [ -f yarn.lock ]; then
found=1
versions=$(awk '/^"?webpack-dev-server@/{flag=1} flag && /^ version /{print $2; flag=0}' yarn.lock | tr -d '"')
for v in $versions; do
if vercmp "$v" "$FIXED_MAJOR.$FIXED_MINOR.$FIXED_PATCH"; then
echo "VULNERABLE: yarn.lock pins webpack-dev-server $v (< 5.2.6)"
vuln=1
fi
done
fi
# pnpm-lock.yaml
if [ -f pnpm-lock.yaml ]; then
found=1
versions=$(grep -oE 'webpack-dev-server@[0-9.]+' pnpm-lock.yaml | cut -d'@' -f2 | sort -u)
for v in $versions; do
if vercmp "$v" "$FIXED_MAJOR.$FIXED_MINOR.$FIXED_PATCH"; then
echo "VULNERABLE: pnpm-lock.yaml pins webpack-dev-server $v (< 5.2.6)"
vuln=1
fi
done
fi
if [ "$found" -eq 0 ]; then
echo "UNKNOWN: no lockfile found in $REPO"
exit 2
fi
if [ "$vuln" -eq 1 ]; then exit 1; fi
echo "PATCHED: all resolved webpack-dev-server versions >= 5.2.6 (or package not present)"
exit 0
If you remember one thing.
webpack-dev-server to ≥ 5.2.6 in the next quarterly dep-refresh PR. Monday morning: (1) run the verification script across your monorepos to enumerate exposed projects, (2) confirm devServer.host is 127.0.0.1 in shared configs, (3) verify browser Private Network Access enforcement is on via MDM. Do NOT page anyone, do NOT open a P1, and do NOT let SBOM tooling escalate this above its real severity — this is a dev-workstation CSRF with an A:L ceiling, not a production RCE.Sources
- webpack-dev-server npm package advisories (Snyk)
- webpack-dev-server CVE list (CVE Details)
- webpack org GitHub Security Advisories
- Chromium Private Network Access spec
- CWE-352: Cross-Site Request Forgery (CSRF)
- webpack-dev-server devServer.host configuration docs
- IBM Security Bulletin — prior webpack-dev-server source exposure CVE (context)
- FIRST EPSS model overview
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.