Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
4.8 KiB
webpack — CWE-407 Disclosure Brief
2026-03-26 · Patch available — awaiting upstream merge
Finding
Three O(n²) defects in webpack's Hot Module Replacement (HMR) runtime. All patched. Patches ready for upstream review. All three defects are in the HMR runtime bundle — JavaScript code shipped to and executed in the browser on every webpack build with HMR enabled.
The Defects
webpack-0001 (PATCHED — MEDIUM-HIGH): lib/hmr/JavascriptHotModuleReplacement.runtime.js:74
// Inside getAffectedModuleEffects() — BFS over module dependency graph:
var outdatedModules = [moduleId];
// ...
if (outdatedModules.indexOf(parentId) !== -1) continue; // O(M) per check
outdatedModules.push(parentId);
outdatedModules is a plain Array. Array.indexOf() is O(M). Called per module per parent edge during BFS traversal. For M affected modules: O(M²) total.
webpack-0002 (PATCHED — MEDIUM): JavascriptHotModuleReplacement.runtime.js:101
// addAllToSet helper — called to merge module lists:
function addAllToSet(a, b) {
for (var i = 0; i < b.length; i++) {
var item = b[i];
if (a.indexOf(item) === -1) a.push(item); // O(N) per insertion
}
}
Array.indexOf() scans the entire accumulator for every new item. For N items: O(N²).
webpack-0003 (PATCHED — MEDIUM): lib/hmr/HotModuleReplacement.runtime.js:60,67
// Inside createRequire() — the hot require() wrapper:
if (module.parents.indexOf(parentId) === -1) // O(P)
module.parents.push(parentId);
// ...
if (me.children.indexOf(request) === -1) // O(C)
me.children.push(request);
Every require() call in the HMR hot path deduplicates parents and children arrays using indexOf. In a large module graph with many require() calls: O(P² + C²).
Complexity Proof
webpack-0001 BFS: For M affected modules in the BFS frontier:
- Each enqueue step checks
outdatedModules.indexOf(parentId): up to M comparisons - Called M times total
- Total: 1 + 2 + ... + M = O(M²)
At M=200: defective=19,900 comparisons, fixed=200. Measured ratio: 100×.
webpack-0002: For N items to add:
- Each
indexOf()scans up to N entries in accumulator - Total: O(N²)
webpack-0003: Per require() call, O(P) parents scan + O(C) children scan.
Impact
webpack is the most widely deployed JavaScript module bundler. webpack 5 powers millions of web applications. HMR is enabled by default in every webpack dev server — it runs in-browser during development. getAffectedModuleEffects() executes on every file save during hot reload. Large frontend applications (thousands of modules, complex dependency graphs) maximize M and hit the worst case on every hot reload.
addAllToSet and the hot require() path run on every HMR update cycle. Applications with shared module graphs — monorepos, design systems, large SPA codebases — are most affected.
The Fix
webpack-0001: Shadow outdatedModules array with a companion Set:
// CWE-407 fix: shadow array with Set for O(1) dedup.
var outdatedModules = [];
var outdatedModulesSet = new Set([moduleId]);
outdatedModules.push(moduleId);
// Replace: if (outdatedModules.indexOf(parentId) !== -1) continue;
if (outdatedModulesSet.has(parentId)) continue;
outdatedModulesSet.add(parentId);
outdatedModules.push(parentId);
webpack-0002: Maintain a companion Set alongside the accumulator array:
// CWE-407 fix: companion Set for O(1) dedup in addAllToSet.
function addAllToSet(a, b) {
if (!a._set) a._set = new Set(a);
for (var i = 0; i < b.length; i++) {
if (!a._set.has(b[i])) { a.push(b[i]); a._set.add(b[i]); }
}
}
webpack-0003: Use Set companions for parents and children:
// CWE-407 fix: Set companions for parents/children in hot require path.
if (!module._parentsSet) module._parentsSet = new Set(module.parents);
if (!module._parentsSet.has(parentId)) {
module.parents.push(parentId);
module._parentsSet.add(parentId);
}
Patch
Fix available:
defects/webpack/patch/webpack-0001-hmr-outdated-set.patchdefects/webpack/patch/webpack-0002-hmr-parents-children-set.patch
Unit test: 4/4 pass. webpack-0001 at M=200: defective=19,900, fixed=200, 100× speedup. webpack-0002 at N=200: same ratio.
What We Ask
A patch is ready for review.
- Confirm receipt and assign a GitHub Security Advisory (webpack/webpack).
- Assess severity — webpack-0001 fires on every hot reload in webpack dev server.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit the webpack team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.