java-topology/whitepaper/outreach/webpack.md
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
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.
2026-03-26 17:11:57 -04:00

125 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`
```javascript
// 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`
```javascript
// 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`
```javascript
// 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`:
```javascript
// 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:
```javascript
// 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:
```javascript
// 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.patch`
- `defects/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.
1. Confirm receipt and assign a GitHub Security Advisory (webpack/webpack).
2. Assess severity — webpack-0001 fires on every hot reload in webpack dev server.
3. Coordinate a disclosure date — we are targeting 90 days from first contact.
4. 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.