java-topology/defects/tomcat/unit/TomcatReplicationValveTest.java
russell@unturf.com 9934133dcf 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
2026-03-27 15:23:43 -04:00

203 lines
8.4 KiB
Java

package unit;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
* Unit test for CWE-407 tomcat-0001:
* ReplicationValve.registerReplicationSession() uses ArrayList.contains()
* for cross-context session deduplication — O(n²) for n registrations.
*
* Run: java -ea -cp . unit.TomcatReplicationValveTest
*/
public class TomcatReplicationValveTest {
// -----------------------------------------------------------------------
// Models — stand-ins for DeltaSession (identity equality, no Tomcat deps)
// -----------------------------------------------------------------------
static final class Session {
final String id;
Session(String id) { this.id = id; }
@Override public String toString() { return "Session(" + id + ")"; }
}
/** Defective: ArrayList-based cross-context session registry (Tomcat original). */
static final class DefectiveRegistry {
private final List<Session> sessions = new ArrayList<>();
long containsProbes = 0;
public void register(Session s) {
// Simulate ArrayList.contains() probe count
containsProbes += sessions.size();
if (!sessions.contains(s)) {
sessions.add(s);
}
}
public List<Session> getSessions() { return sessions; }
}
/** Fixed: LinkedHashSet-based registry — O(1) dedup, no contains() guard. */
static final class FixedRegistry {
private final LinkedHashSet<Session> sessions = new LinkedHashSet<>();
long containsProbes = 0;
public void register(Session s) {
containsProbes += 1; // O(1) hash probe
sessions.add(s); // Set.add() is idempotent
}
public LinkedHashSet<Session> getSessions() { return sessions; }
}
// -----------------------------------------------------------------------
// Test 1 — correctness: same unique sessions survive dedup
// -----------------------------------------------------------------------
static void test1_correctness() {
int N = 20;
Session[] allSessions = new Session[N];
for (int i = 0; i < N; i++) allSessions[i] = new Session("s" + i);
DefectiveRegistry def = new DefectiveRegistry();
FixedRegistry fix = new FixedRegistry();
// Register each session twice (duplicate registrations)
for (Session s : allSessions) { def.register(s); fix.register(s); }
for (Session s : allSessions) { def.register(s); fix.register(s); }
assert def.getSessions().size() == N
: "tomcat-0001 correctness: defective size=" + def.getSessions().size() + " expected=" + N;
assert fix.getSessions().size() == N
: "tomcat-0001 correctness: fixed size=" + fix.getSessions().size() + " expected=" + N;
// Same sessions in same order
List<Session> defList = def.getSessions();
List<Session> fixList = new ArrayList<>(fix.getSessions());
assert defList.equals(fixList)
: "tomcat-0001 correctness: session lists differ";
System.out.println("PASS test1_correctness: " + N + " unique sessions after 2x registration each");
}
// -----------------------------------------------------------------------
// Test 2 — complexity: O(n²) probes vs O(n) probes
// -----------------------------------------------------------------------
static void test2_complexity_ratio() {
// Small: N=50, Large: N=500 — unique sessions each time (no early exit)
int small = 50;
int large = 500;
DefectiveRegistry defSmall = new DefectiveRegistry();
DefectiveRegistry defLarge = new DefectiveRegistry();
FixedRegistry fixSmall = new FixedRegistry();
FixedRegistry fixLarge = new FixedRegistry();
// Register N unique sessions (all new — worst case for defective: no early miss)
for (int i = 0; i < small; i++) {
Session s = new Session("s" + i);
defSmall.register(s);
fixSmall.register(s);
}
for (int i = 0; i < large; i++) {
Session s = new Session("s" + i);
defLarge.register(s);
fixLarge.register(s);
}
double defRatio = (double) defLarge.containsProbes / Math.max(defSmall.containsProbes, 1);
double fixRatio = (double) fixLarge.containsProbes / Math.max(fixSmall.containsProbes, 1);
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);
// 10x input → ~100x probes (quadratic)
assert defRatio > 50.0
: "tomcat-0001 complexity: defective ratio should be >50x at 10x scale, got " + defRatio;
// 10x input → ~10x probes (linear)
assert fixRatio < 20.0
: "tomcat-0001 complexity: fixed ratio should be <20x at 10x scale, got " + fixRatio;
assert defRatio > fixRatio * 3
: "tomcat-0001 complexity: defective should grow much faster, def=" + defRatio + " fix=" + fixRatio;
System.out.printf("PASS test2_complexity_ratio: defective=%.0fx fixed=%.0fx%n", defRatio, fixRatio);
}
// -----------------------------------------------------------------------
// Test 3 — absolute counts at N=200
// -----------------------------------------------------------------------
static void test3_absolute_counts() {
int N = 200;
DefectiveRegistry def = new DefectiveRegistry();
FixedRegistry fix = new FixedRegistry();
for (int i = 0; i < N; i++) {
Session s = new Session("s" + i);
def.register(s);
fix.register(s);
}
// Defective: probes = 0+1+2+...+(N-1) = N*(N-1)/2
long expectedDefMin = (long) N * (N - 1) / 2;
assert def.containsProbes >= expectedDefMin
: "tomcat-0001 counts: defective probes=" + def.containsProbes + " expected>=" + expectedDefMin;
// Fixed: exactly N probes (one per registration)
assert fix.containsProbes == N
: "tomcat-0001 counts: fixed probes=" + fix.containsProbes + " expected=" + N;
long speedup = def.containsProbes / fix.containsProbes;
System.out.printf("PASS test3_absolute_counts: defective=%d fixed=%d speedup=%dx%n",
def.containsProbes, fix.containsProbes, speedup);
}
// -----------------------------------------------------------------------
// Test 4 — duplicate registration preserves single entry
// -----------------------------------------------------------------------
static void test4_duplicate_single_entry() {
Session s = new Session("shared");
FixedRegistry fix = new FixedRegistry();
for (int i = 0; i < 50; i++) fix.register(s);
assert fix.getSessions().size() == 1
: "tomcat-0001 dedup: expected 1 entry after 50 identical registrations, got " + fix.getSessions().size();
assert fix.getSessions().contains(s)
: "tomcat-0001 dedup: session not found after registration";
System.out.println("PASS test4_duplicate_single_entry: 50 registrations → 1 entry");
}
// -----------------------------------------------------------------------
// main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("=== TomcatReplicationValveTest — CWE-407 tomcat-0001 ===");
int passed = 0;
int failed = 0;
Runnable[] tests = {
TomcatReplicationValveTest::test1_correctness,
TomcatReplicationValveTest::test2_complexity_ratio,
TomcatReplicationValveTest::test3_absolute_counts,
TomcatReplicationValveTest::test4_duplicate_single_entry,
};
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);
}
}