whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild

Add 88 new defect entries to HIGH and MEDIUM tables:
  HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
        vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
        tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
        allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
        mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
        linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
        perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002

  MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
          cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
          pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
          ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
          r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
          ovs-0001, onos-0003, odl-0002, jetty-0001

PDF: 976K
This commit is contained in:
russell@unturf.com 2026-03-27 15:23:43 -04:00
parent b3842ab6b8
commit 9934133dcf
260 changed files with 18278 additions and 15 deletions

View file

@ -0,0 +1,370 @@
package unit;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
* Unit tests for CWE-407 defects in Spring Framework.
*
* spring-0003 (HIGH): AbstractApplicationEventMulticaster.retrieveApplicationListeners()
* builds allListeners as ArrayList. Inside the O(L) listenerBeans loop it calls
* allListeners.contains() twice (lines 279, 285). Total O(L × (P+L)) = O(n²).
* Hit on every event dispatch cache-miss.
*
* spring-0004 (MEDIUM): DefaultListenerRetriever.getApplicationListeners() has the
* same pattern ArrayList allListeners, allListeners.contains(listener) in loop
* over applicationListenerBeans (line 512).
*
* Run: java -ea -cp . unit.SpringEventMulticasterTest
*/
public class SpringEventMulticasterTest {
// -----------------------------------------------------------------------
// Listener model identity-equal objects (like ApplicationListener instances)
// -----------------------------------------------------------------------
static final class Listener {
final String name;
final boolean isProxy;
final Listener target; // non-null if this is a proxy
Listener(String name) { this.name = name; this.isProxy = false; this.target = null; }
Listener(String name, Listener target) { this.name = name; this.isProxy = true; this.target = target; }
@Override public String toString() { return (isProxy ? "Proxy(" : "Listener(") + name + ")"; }
}
// -----------------------------------------------------------------------
// spring-0003 model: retrieveApplicationListeners() ArrayList vs LinkedHashSet
// -----------------------------------------------------------------------
static final class DefectiveRetriever {
long containsProbes = 0;
/** Models the defective retrieveApplicationListeners() inner loop. */
List<Listener> retrieve(List<Listener> programmatic, List<Listener[]> beanListeners) {
// allListeners starts with programmatic listeners
List<Listener> allListeners = new ArrayList<>(programmatic);
for (Listener[] pair : beanListeners) {
// pair[0] = proxy, pair[1] = unwrapped target (or same if not proxy)
Listener listener = pair[0];
Listener unwrapped = pair[1];
if (listener != unwrapped) {
// Line 279: allListeners.contains(unwrappedListener)
containsProbes += allListeners.size(); // O(n) scan
if (allListeners.contains(unwrapped)) {
allListeners.remove(unwrapped);
allListeners.add(listener);
continue;
}
}
// Line 285: !allListeners.contains(listener)
containsProbes += allListeners.size(); // O(n) scan
if (!allListeners.contains(listener)) {
allListeners.add(listener);
}
}
return allListeners;
}
}
static final class FixedRetriever {
long containsProbes = 0;
/** Models the fixed retrieveApplicationListeners() using LinkedHashSet. */
List<Listener> retrieve(List<Listener> programmatic, List<Listener[]> beanListeners) {
LinkedHashSet<Listener> allListenerSet = new LinkedHashSet<>(programmatic);
for (Listener[] pair : beanListeners) {
Listener listener = pair[0];
Listener unwrapped = pair[1];
if (listener != unwrapped) {
containsProbes += 1; // O(1) LinkedHashSet.remove()
if (allListenerSet.remove(unwrapped)) {
allListenerSet.add(listener);
continue;
}
}
containsProbes += 1; // O(1) LinkedHashSet.contains()
if (!allListenerSet.contains(listener)) {
allListenerSet.add(listener);
}
}
return new ArrayList<>(allListenerSet);
}
}
// -----------------------------------------------------------------------
// spring-0004 model: DefaultListenerRetriever.getApplicationListeners()
// -----------------------------------------------------------------------
static final class DefectiveDefaultRetriever {
long containsProbes = 0;
List<Listener> getListeners(List<Listener> programmatic, List<Listener> beanListeners) {
List<Listener> allListeners = new ArrayList<>(programmatic);
for (Listener l : beanListeners) {
containsProbes += allListeners.size(); // O(n) scan line 512
if (!allListeners.contains(l)) {
allListeners.add(l);
}
}
return allListeners;
}
}
static final class FixedDefaultRetriever {
long containsProbes = 0;
List<Listener> getListeners(List<Listener> programmatic, List<Listener> beanListeners) {
LinkedHashSet<Listener> allListenerSet = new LinkedHashSet<>(programmatic);
for (Listener l : beanListeners) {
containsProbes += 1; // O(1) LinkedHashSet.add()
allListenerSet.add(l);
}
return new ArrayList<>(allListenerSet);
}
}
// -----------------------------------------------------------------------
// Test 1 spring-0003 correctness: proxy replacement and dedup
// -----------------------------------------------------------------------
static void test1_spring0003_correctness() {
Listener a = new Listener("A");
Listener b = new Listener("B");
Listener bProxy = new Listener("B-proxy", b); // proxy wrapping b
List<Listener> programmatic = List.of(a, b);
// beanListeners: a proxy for b (should replace b in the list)
List<Listener[]> beanListeners = new ArrayList<>();
beanListeners.add(new Listener[]{bProxy, b});
DefectiveRetriever def = new DefectiveRetriever();
FixedRetriever fix = new FixedRetriever();
List<Listener> defResult = def.retrieve(programmatic, beanListeners);
List<Listener> fixResult = fix.retrieve(programmatic, beanListeners);
assert defResult.size() == fixResult.size()
: "spring-0003 correctness: size differs def=" + defResult.size() + " fix=" + fixResult.size();
assert defResult.equals(fixResult)
: "spring-0003 correctness: listener lists differ: def=" + defResult + " fix=" + fixResult;
// b should be replaced by bProxy
assert !fixResult.contains(b)
: "spring-0003 correctness: bare b should be replaced by proxy";
assert fixResult.contains(bProxy)
: "spring-0003 correctness: bProxy should be present";
System.out.println("PASS test1_spring0003_correctness: proxy replacement correct, size=" + fixResult.size());
}
// -----------------------------------------------------------------------
// Test 2 spring-0003 complexity: O(n²) vs O(n) probes
// -----------------------------------------------------------------------
static void test2_spring0003_complexity_ratio() {
int small = 50;
int large = 500;
// Build inputs: P programmatic listeners, L bean listeners (all unique, no proxies)
List<Listener> smallProg = new ArrayList<>(), largeProg = new ArrayList<>();
List<Listener[]> smallBean = new ArrayList<>(), largeBean = new ArrayList<>();
for (int i = 0; i < small; i++) {
smallProg.add(new Listener("prog-" + i));
}
for (int i = 0; i < small; i++) {
Listener l = new Listener("bean-" + i);
smallBean.add(new Listener[]{l, l}); // no proxy
}
for (int i = 0; i < large; i++) {
largeProg.add(new Listener("prog-" + i));
}
for (int i = 0; i < large; i++) {
Listener l = new Listener("bean-" + i);
largeBean.add(new Listener[]{l, l});
}
DefectiveRetriever defSmall = new DefectiveRetriever();
DefectiveRetriever defLarge = new DefectiveRetriever();
FixedRetriever fixSmall = new FixedRetriever();
FixedRetriever fixLarge = new FixedRetriever();
defSmall.retrieve(smallProg, smallBean);
defLarge.retrieve(largeProg, largeBean);
fixSmall.retrieve(smallProg, smallBean);
fixLarge.retrieve(largeProg, largeBean);
double defRatio = (double) defLarge.containsProbes / defSmall.containsProbes;
double fixRatio = (double) fixLarge.containsProbes / fixSmall.containsProbes;
System.out.printf(" defective probes: small=%d large=%d ratio=%.1fx%n",
defSmall.containsProbes, defLarge.containsProbes, defRatio);
System.out.printf(" fixed probes: small=%d large=%d ratio=%.1fx%n",
fixSmall.containsProbes, fixLarge.containsProbes, fixRatio);
assert defRatio > 50.0
: "spring-0003 complexity: defective ratio should be >50x, got " + defRatio;
assert fixRatio < 20.0
: "spring-0003 complexity: fixed ratio should be <20x, got " + fixRatio;
System.out.printf("PASS test2_spring0003_complexity_ratio: defective=%.0fx fixed=%.0fx%n", defRatio, fixRatio);
}
// -----------------------------------------------------------------------
// Test 3 spring-0003 absolute counts at N=200
// -----------------------------------------------------------------------
static void test3_spring0003_absolute_counts() {
int P = 100, L = 100; // 100 programmatic + 100 bean listeners, all unique
List<Listener> prog = new ArrayList<>();
List<Listener[]> bean = new ArrayList<>();
for (int i = 0; i < P; i++) prog.add(new Listener("prog-" + i));
for (int i = 0; i < L; i++) {
Listener l = new Listener("bean-" + i);
bean.add(new Listener[]{l, l});
}
DefectiveRetriever def = new DefectiveRetriever();
FixedRetriever fix = new FixedRetriever();
def.retrieve(prog, bean);
fix.retrieve(prog, bean);
// Defective: each of L iterations checks list of size (P+0..L-1) twice
// Lower bound: L * P probes (at minimum the initial P listeners are scanned)
long expectedDefMin = (long) L * P;
assert def.containsProbes >= expectedDefMin
: "spring-0003 counts: defective probes=" + def.containsProbes + " expected>=" + expectedDefMin;
// Fixed: exactly L probes (one per bean listener)
assert fix.containsProbes == L
: "spring-0003 counts: fixed probes=" + fix.containsProbes + " expected=" + L;
long speedup = def.containsProbes / fix.containsProbes;
System.out.printf("PASS test3_spring0003_absolute_counts: defective=%d fixed=%d speedup=%dx%n",
def.containsProbes, fix.containsProbes, speedup);
}
// -----------------------------------------------------------------------
// Test 4 spring-0004 correctness
// -----------------------------------------------------------------------
static void test4_spring0004_correctness() {
Listener a = new Listener("A"), b = new Listener("B"), c = new Listener("C");
List<Listener> programmatic = List.of(a, b);
List<Listener> beanListeners = List.of(b, c); // b is duplicate
DefectiveDefaultRetriever def = new DefectiveDefaultRetriever();
FixedDefaultRetriever fix = new FixedDefaultRetriever();
List<Listener> defResult = def.getListeners(programmatic, beanListeners);
List<Listener> fixResult = fix.getListeners(programmatic, beanListeners);
assert defResult.size() == 3
: "spring-0004 correctness: defective size=" + defResult.size() + " expected=3";
assert fixResult.size() == 3
: "spring-0004 correctness: fixed size=" + fixResult.size() + " expected=3";
assert defResult.equals(fixResult)
: "spring-0004 correctness: results differ def=" + defResult + " fix=" + fixResult;
System.out.println("PASS test4_spring0004_correctness: size=3, dedup correct");
}
// -----------------------------------------------------------------------
// Test 5 spring-0004 complexity ratio
// -----------------------------------------------------------------------
static void test5_spring0004_complexity_ratio() {
int small = 50, large = 500;
List<Listener> smallProg = new ArrayList<>(), largeProg = new ArrayList<>();
List<Listener> smallBean = new ArrayList<>(), largeBean = new ArrayList<>();
for (int i = 0; i < small; i++) smallProg.add(new Listener("p" + i));
for (int i = 0; i < small; i++) smallBean.add(new Listener("b" + i));
for (int i = 0; i < large; i++) largeProg.add(new Listener("p" + i));
for (int i = 0; i < large; i++) largeBean.add(new Listener("b" + i));
DefectiveDefaultRetriever defSmall = new DefectiveDefaultRetriever();
DefectiveDefaultRetriever defLarge = new DefectiveDefaultRetriever();
FixedDefaultRetriever fixSmall = new FixedDefaultRetriever();
FixedDefaultRetriever fixLarge = new FixedDefaultRetriever();
defSmall.getListeners(smallProg, smallBean);
defLarge.getListeners(largeProg, largeBean);
fixSmall.getListeners(smallProg, smallBean);
fixLarge.getListeners(largeProg, largeBean);
double defRatio = (double) defLarge.containsProbes / defSmall.containsProbes;
double fixRatio = (double) fixLarge.containsProbes / fixSmall.containsProbes;
System.out.printf(" defective probes: small=%d large=%d ratio=%.1fx%n",
defSmall.containsProbes, defLarge.containsProbes, defRatio);
System.out.printf(" fixed probes: small=%d large=%d ratio=%.1fx%n",
fixSmall.containsProbes, fixLarge.containsProbes, fixRatio);
assert defRatio > 50.0
: "spring-0004 complexity: defective ratio should be >50x, got " + defRatio;
assert fixRatio < 20.0
: "spring-0004 complexity: fixed ratio should be <20x, got " + fixRatio;
System.out.printf("PASS test5_spring0004_complexity_ratio: defective=%.0fx fixed=%.0fx%n", defRatio, fixRatio);
}
// -----------------------------------------------------------------------
// Test 6 spring-0004 absolute counts at N=200
// -----------------------------------------------------------------------
static void test6_spring0004_absolute_counts() {
int P = 100, L = 100;
List<Listener> prog = new ArrayList<>(), bean = new ArrayList<>();
for (int i = 0; i < P; i++) prog.add(new Listener("p" + i));
for (int i = 0; i < L; i++) bean.add(new Listener("b" + i)); // all unique
DefectiveDefaultRetriever def = new DefectiveDefaultRetriever();
FixedDefaultRetriever fix = new FixedDefaultRetriever();
def.getListeners(prog, bean);
fix.getListeners(prog, bean);
long expectedDefMin = (long) L * P;
assert def.containsProbes >= expectedDefMin
: "spring-0004 counts: defective probes=" + def.containsProbes + " expected>=" + expectedDefMin;
assert fix.containsProbes == L
: "spring-0004 counts: fixed probes=" + fix.containsProbes + " expected=" + L;
System.out.printf("PASS test6_spring0004_absolute_counts: defective=%d fixed=%d speedup=%dx%n",
def.containsProbes, fix.containsProbes, def.containsProbes / fix.containsProbes);
}
// -----------------------------------------------------------------------
// main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("=== SpringEventMulticasterTest — CWE-407 spring-0003 / spring-0004 ===");
int passed = 0, failed = 0;
Runnable[] tests = {
SpringEventMulticasterTest::test1_spring0003_correctness,
SpringEventMulticasterTest::test2_spring0003_complexity_ratio,
SpringEventMulticasterTest::test3_spring0003_absolute_counts,
SpringEventMulticasterTest::test4_spring0004_correctness,
SpringEventMulticasterTest::test5_spring0004_complexity_ratio,
SpringEventMulticasterTest::test6_spring0004_absolute_counts,
};
for (Runnable test : tests) {
try {
test.run();
passed++;
} catch (AssertionError e) {
System.out.println("FAIL: " + e.getMessage());
failed++;
}
}
System.out.println("---");
System.out.println("Results: " + passed + " passed, " + failed + " failed");
if (failed > 0) System.exit(1);
}
}