# Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.json custom stats (normalizeStats)

Unguarded for...in over untrusted browserslist-stats.json keys lets inherited Object.prototype names crash normalizeStats, or __proto__ write the prototype, DoSing any browserslist() call.

- Payout: Research
- Severity: high
- Program: Browserslist
- Found by: iaohkut-from-NightWolf-Team
- Disclosed: 2026-09-01
- CVE: CVE-2026-73088
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-73wf-gq98-2v4g
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-73wf-gq98-2v4g
- Tags: area:oss domain:oss impact:dos kind:research platform:ghsa sev:high tech:nodejs technique:incomplete-fix technique:source-review vuln:dos vuln:prototype-pollution

**Target:** npm library node.js normalizeStats()/getStat() stats handling
**Root cause:** normalizeStats does an unguarded for...in over untrusted stats keys and uses them for bracket access on data and assignment on a plain {}; inherited Object.prototype names resolve truthy in data[i] (crash on .versions.length) and __proto__ assignment invokes the prototype setter.
**Steps:**
1. Place `browserslist-stats.json` containing `{"toString": {"onekey": 5}, "chrome": {"100": 50}}` anywhere between project root and filesystem root (or pass via `--stats=`/opts.stats).
2. Call `browserslist('defaults')` — getStat runs unconditionally and auto-discovers the file.
3. normalizeStats iterates key `toString`; `data['toString']` resolves to inherited function, `data[i].versions.length` throws uncaught TypeError.
4. Swap key to `__proto__` with one sub-key to instead trigger the Object.prototype.__proto__ setter write on `normalized`.
**How found:** Systematic review of prototype-pollution-adjacent patterns after finding two unrelated algorithmic-complexity issues; noticed copyObject() in index.js already guarded __proto__/constructor/prototype but that guard was never applied to normalizeStats.
**Exploitation:** Attacker submits a PR or compromises a dependency adding the poisoned stats file; victim's build/CI (Autoprefixer, Babel preset-env, Stylelint, PostCSS) crashes on first browserslist() call for any query, no auth needed.
**Insight:** When a codebase already guards one for...in + bracket-write helper against __proto__/constructor/prototype, grep for sibling functions doing the same pattern — the fix is rarely propagated. Also test inherited Object.prototype member names (toString, valueOf, hasOwnProperty) as keys, not just __proto__, since truthy inherited lookups cause crashes.

