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.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,46 @@
diff --git a/lib/hmr/JavascriptHotModuleReplacement.runtime.js b/lib/hmr/JavascriptHotModuleReplacement.runtime.js
index xxxxxxx..xxxxxxx 100644
--- a/lib/hmr/JavascriptHotModuleReplacement.runtime.js
+++ b/lib/hmr/JavascriptHotModuleReplacement.runtime.js
@@ -26,7 +26,8 @@ module.exports = function () {
function getAffectedModuleEffects(updateModuleId) {
- var outdatedModules = [updateModuleId];
+ // CWE-407 fix: use Set for O(1) membership checks in BFS traversal.
+ var outdatedModulesSet = new Set([updateModuleId]);
+ var outdatedModules = [updateModuleId]; // kept for ordered result only
var outdatedDependencies = {};
var queue = outdatedModules.map(function (id) {
@@ -70,9 +71,9 @@ module.exports = function () {
for (var i = 0; i < module.parents.length; i++) {
var parentId = module.parents[i];
var parent = $moduleCache$[parentId];
if (!parent) continue;
if (parent.hot._declinedDependencies[moduleId]) {
return { type: "declined", chain: chain.concat([parentId]),
moduleId: moduleId, parentId: parentId };
}
- if (outdatedModules.indexOf(parentId) !== -1) continue;
+ if (outdatedModulesSet.has(parentId)) continue;
if (parent.hot._acceptedDependencies[moduleId]) {
if (!outdatedDependencies[parentId])
outdatedDependencies[parentId] = [];
@@ -81,6 +82,7 @@ module.exports = function () {
}
delete outdatedDependencies[parentId];
outdatedModules.push(parentId);
+ outdatedModulesSet.add(parentId);
queue.push({ chain: chain.concat([parentId]), id: parentId });
}
}
@@ -96,11 +98,12 @@ module.exports = function () {
}
function addAllToSet(a, b) {
for (var i = 0; i < b.length; i++) {
var item = b[i];
- if (a.indexOf(item) === -1) a.push(item);
+ if (!a._set) a._set = new Set(a);
+ if (!a._set.has(item)) { a.push(item); a._set.add(item); }
}
}

View file

@ -0,0 +1,37 @@
diff --git a/lib/hmr/HotModuleReplacement.runtime.js b/lib/hmr/HotModuleReplacement.runtime.js
index xxxxxxx..xxxxxxx 100644
--- a/lib/hmr/HotModuleReplacement.runtime.js
+++ b/lib/hmr/HotModuleReplacement.runtime.js
@@ -42,7 +42,8 @@ module.exports = function (parentHot) {
hot = {
// ...
};
module.parents = currentParents;
- module.children = [];
+ // CWE-407 fix: use Set-backed arrays for O(1) dedup in require() hot path.
+ module.children = [];
+ module._childrenSet = new Set();
}
// ...
@@ -57,10 +57,10 @@ module.exports = function (parentHot) {
if (me.hot.active) {
if (installedModules[request]) {
var parents = installedModules[request].parents;
- if (parents.indexOf(moduleId) === -1) {
+ if (!installedModules[request]._parentsSet) installedModules[request]._parentsSet = new Set(parents);
+ if (!installedModules[request]._parentsSet.has(moduleId)) {
parents.push(moduleId);
+ installedModules[request]._parentsSet.add(moduleId);
}
} else {
currentParents = [moduleId];
currentChildModule = request;
}
- if (me.children.indexOf(request) === -1) {
+ if (!me._childrenSet.has(request)) {
me.children.push(request);
+ me._childrenSet.add(request);
}
}

View file

@ -0,0 +1,374 @@
package unit;
import java.util.*;
/**
* Unit test for webpack-0001/0002/0003: CWE-407 in webpack HMR runtime.
*
* webpack-0001 (MEDIUM-HIGH):
* File: lib/hmr/JavascriptHotModuleReplacement.runtime.js:74
* Symbol: getAffectedModuleEffects — `outdatedModules.indexOf(parentId)`
* Defect: BFS traversal over a module dependency graph uses an Array for the
* outdatedModules visited set. For each queued module, for each parent,
* indexOf scans the entire array: O(M) per check, O(M²) total.
* Fix: Shadow with a Set; check becomes Set.has(), O(1).
*
* webpack-0002 (MEDIUM):
* File: lib/hmr/JavascriptHotModuleReplacement.runtime.js:101
* Symbol: addAllToSet — `a.indexOf(item)`
* Defect: Result-merging helper deduplicates by scanning the accumulator
* array on every insertion: O(N) per item, O(N²) total for N items.
* Fix: Maintain a companion Set on the accumulator array; check is O(1).
*
* webpack-0003 (MEDIUM):
* File: lib/hmr/HotModuleReplacement.runtime.js:60,67
* Symbol: createRequire — `parents.indexOf(moduleId)`, `me.children.indexOf(request)`
* Defect: Every require() call in the HMR hot path deduplicates parents and
* children using indexOf on plain arrays: O(P) and O(C) per call.
* Fix: Attach Set companions (_parentsSet, _childrenSet); O(1) per check.
*
* Modeled here in Java:
* JS Array + indexOf ≡ List<Integer> + contains/indexOf (defective)
* JS Set + has ≡ java.util.Set + contains (fixed)
* Comparison counts tracked at the membership-test site.
*
* Expected at M=200:
* defective BFS comparisons ≈ M² = 40 000
* fixed BFS comparisons ≈ M = 200
* ratio > 10×
*/
public class WebpackHmrTest {
// =========================================================================
// webpack-0001 model: BFS with outdatedModules as Array vs Set
// =========================================================================
/**
* Defective BFS: outdatedModules is a plain List.
* For each (module, parent) pair we call list.contains(), which is O(size).
* comparisons counts every element examined during indexOf scans.
*/
static class DefectiveBfs {
long comparisons = 0;
/**
* Simulate getAffectedModuleEffects: process a chain of M modules where
* each module has one parent, and the parent is always "not yet in
* outdatedModules" (worst-case: no early-exit, full scan every time).
*
* Graph: 0 → 1 → 2 → ... → M-1 (child → parent direction).
* BFS starts from module 0, propagates up.
*/
List<Integer> run(int M) {
List<Integer> outdatedModules = new ArrayList<>();
outdatedModules.add(0);
// Queue: (moduleId). We process each and check its single parent.
Queue<Integer> queue = new ArrayDeque<>();
queue.add(0);
while (!queue.isEmpty()) {
int moduleId = queue.poll();
int parentId = moduleId + 1; // parent in linear chain
if (parentId >= M) continue; // no parent beyond chain end
// outdatedModules.indexOf(parentId) — O(current size) scan
boolean found = false;
for (int x : outdatedModules) {
comparisons++;
if (x == parentId) { found = true; break; }
}
if (found) continue;
outdatedModules.add(parentId);
queue.add(parentId);
}
return outdatedModules;
}
}
/**
* Fixed BFS: shadow outdatedModules with a Set for O(1) membership.
* comparisons counts one hash-probe per Set.contains() call.
*/
static class FixedBfs {
long comparisons = 0;
List<Integer> run(int M) {
List<Integer> outdatedModules = new ArrayList<>();
Set<Integer> outdatedSet = new HashSet<>();
outdatedModules.add(0);
outdatedSet.add(0);
Queue<Integer> queue = new ArrayDeque<>();
queue.add(0);
while (!queue.isEmpty()) {
int moduleId = queue.poll();
int parentId = moduleId + 1;
if (parentId >= M) continue;
// Set.contains(parentId) — O(1)
comparisons++;
if (outdatedSet.contains(parentId)) continue;
outdatedModules.add(parentId);
outdatedSet.add(parentId);
queue.add(parentId);
}
return outdatedModules;
}
}
// =========================================================================
// webpack-0002 model: addAllToSet with Array indexOf vs Set companion
// =========================================================================
/**
* Defective addAllToSet: accumulator is a plain List; every insertion
* scans the list to deduplicate.
* comparisons counts every element examined during indexOf scans.
*/
static long addAllToSetDefective(List<Integer> a, List<Integer> b,
long[] comparisons) {
for (int item : b) {
// a.indexOf(item) — O(a.size()) scan
boolean found = false;
for (int x : a) {
comparisons[0]++;
if (x == item) { found = true; break; }
}
if (!found) a.add(item);
}
return comparisons[0];
}
/**
* Fixed addAllToSet: companion Set maintained alongside the List.
* comparisons counts one hash-probe per Set.contains() call.
*/
static long addAllToSetFixed(List<Integer> a, Set<Integer> aSet,
List<Integer> b, long[] comparisons) {
for (int item : b) {
comparisons[0]++; // O(1) Set.contains
if (!aSet.contains(item)) {
a.add(item);
aSet.add(item);
}
}
return comparisons[0];
}
// =========================================================================
// webpack-0003 model: parents/children dedup in require() hot path
// =========================================================================
/**
* Defective require(): parents and children deduped via indexOf on plain arrays.
* Simulates N require() calls where each call checks both parents and children.
* comparisons counts every element examined during indexOf scans.
*/
static long simulateRequireDefective(int N) {
long comparisons = 0;
List<Integer> parents = new ArrayList<>();
List<Integer> children = new ArrayList<>();
for (int moduleId = 0; moduleId < N; moduleId++) {
int request = moduleId + 1000; // distinct child module IDs
// parents.indexOf(moduleId) — O(parents.size())
boolean foundParent = false;
for (int x : parents) {
comparisons++;
if (x == moduleId) { foundParent = true; break; }
}
if (!foundParent) parents.add(moduleId);
// me.children.indexOf(request) — O(children.size())
boolean foundChild = false;
for (int x : children) {
comparisons++;
if (x == request) { foundChild = true; break; }
}
if (!foundChild) children.add(request);
}
return comparisons;
}
/**
* Fixed require(): Set companions for O(1) dedup.
* comparisons counts one hash-probe per check.
*/
static long simulateRequireFixed(int N) {
long comparisons = 0;
List<Integer> parents = new ArrayList<>();
Set<Integer> parentsSet = new HashSet<>();
List<Integer> children = new ArrayList<>();
Set<Integer> childrenSet = new HashSet<>();
for (int moduleId = 0; moduleId < N; moduleId++) {
int request = moduleId + 1000;
// parentsSet.contains(moduleId) — O(1)
comparisons++;
if (!parentsSet.contains(moduleId)) {
parents.add(moduleId);
parentsSet.add(moduleId);
}
// childrenSet.contains(request) — O(1)
comparisons++;
if (!childrenSet.contains(request)) {
children.add(request);
childrenSet.add(request);
}
}
return comparisons;
}
// =========================================================================
// Tests
// =========================================================================
/**
* Test 1 — Correctness: defective and fixed BFS produce identical module sets.
*/
static void testBfsCorrectnessMatch() {
int M = 50;
DefectiveBfs def = new DefectiveBfs();
FixedBfs fix = new FixedBfs();
List<Integer> defResult = def.run(M);
List<Integer> fixResult = fix.run(M);
Set<Integer> defSet = new HashSet<>(defResult);
Set<Integer> fixSet = new HashSet<>(fixResult);
assert defSet.equals(fixSet)
: "BFS output mismatch: defective=" + defSet + " fixed=" + fixSet;
assert defResult.size() == M
: "expected " + M + " outdated modules; got " + defResult.size();
System.out.println("PASS testBfsCorrectnessMatch");
}
/**
* Test 2 — webpack-0001: ratio of defective vs fixed BFS comparisons > 10x at M=200.
*
* Defective: each of M modules triggers a scan of the growing outdatedModules
* list (size 0..M-1), summing to M*(M-1)/2 comparisons ~ O(M²).
* Fixed: each module triggers exactly 1 Set probe, summing to M ~ O(M).
*/
static void testBfsRatioAtScale() {
int M = 200;
DefectiveBfs def = new DefectiveBfs();
FixedBfs fix = new FixedBfs();
def.run(M);
fix.run(M);
long defComp = def.comparisons;
long fixComp = fix.comparisons;
double ratio = (double) defComp / fixComp;
// Defective: sum 0+1+...+(M-1) = M*(M-1)/2
long expectedDef = (long) M * (M - 1) / 2;
assert defComp == expectedDef
: "defective BFS comparisons should be M*(M-1)/2=" + expectedDef
+ "; got " + defComp;
// Fixed: M-1 probes (one per module that has a parent to check;
// the last module in the chain has no parent, so no probe is issued)
assert fixComp == M - 1
: "fixed BFS comparisons should be M-1=" + (M - 1) + "; got " + fixComp;
assert ratio > 10.0
: "ratio should be >10x at M=200; got " + ratio;
System.out.printf(
"PASS testBfsRatioAtScale (defective=%d, fixed=%d, ratio=%.1fx)%n",
defComp, fixComp, ratio);
}
/**
* Test 3 — webpack-0002: addAllToSet defective is O(N²), fixed is O(N).
*
* Merge N unique items into an accumulator. Defective scans the accumulator
* before each insert (size 0..N-1), costing N*(N-1)/2. Fixed costs N probes.
*/
static void testAddAllToSetRatio() {
int N = 200;
// Build source list of N unique items
List<Integer> source = new ArrayList<>();
for (int i = 0; i < N; i++) source.add(i);
// Defective
long[] defComp = {0};
List<Integer> defAcc = new ArrayList<>();
addAllToSetDefective(defAcc, source, defComp);
// Fixed
long[] fixComp = {0};
List<Integer> fixAcc = new ArrayList<>();
Set<Integer> fixSet = new HashSet<>();
addAllToSetFixed(fixAcc, fixSet, source, fixComp);
assert defAcc.size() == N && fixAcc.size() == N
: "both accumulators should have " + N + " items";
assert new HashSet<>(defAcc).equals(new HashSet<>(fixAcc))
: "accumulator contents differ";
double ratio = (double) defComp[0] / fixComp[0];
assert ratio > 10.0
: "addAllToSet ratio should be >10x at N=200; got " + ratio;
System.out.printf(
"PASS testAddAllToSetRatio (defective=%d, fixed=%d, ratio=%.1fx)%n",
defComp[0], fixComp[0], ratio);
}
/**
* Test 4 — webpack-0003: require() hot path defective is O(N²), fixed is O(N).
*
* N require() calls, each adding a unique moduleId to parents and a unique
* request to children. Defective: scans grow 0..N-1 for each; fixed: O(1) probes.
*/
static void testRequireHotPathRatio() {
int N = 200;
long defComp = simulateRequireDefective(N);
long fixComp = simulateRequireFixed(N);
double ratio = (double) defComp / fixComp;
// Defective: two indexOf scans per call, sizes grow 0..N-1
// Sum for parents: 0+1+...+(N-1) = N*(N-1)/2; same for children.
long expectedDef = (long) N * (N - 1); // two arrays: 2 × N*(N-1)/2
assert defComp == expectedDef
: "defective require comparisons should be N*(N-1)=" + expectedDef
+ "; got " + defComp;
// Fixed: exactly 2 probes per call (one per Set)
long expectedFix = 2L * N;
assert fixComp == expectedFix
: "fixed require comparisons should be 2*N=" + expectedFix
+ "; got " + fixComp;
assert ratio > 10.0
: "require() ratio should be >10x at N=200; got " + ratio;
System.out.printf(
"PASS testRequireHotPathRatio (defective=%d, fixed=%d, ratio=%.1fx)%n",
defComp, fixComp, ratio);
}
// =========================================================================
public static void main(String[] args) {
testBfsCorrectnessMatch();
testBfsRatioAtScale();
testAddAllToSetRatio();
testRequireHotPathRatio();
System.out.println("All webpack HMR tests passed.");
}
}