java-topology/defects/conduit/unit/ConduitTest.java
russell@unturf.com ec186e286c element-web-0003/0004 + conduit-0001: CWE-407 deep scan — 3 new defects, 6/6 PASS
element-web-0003: TextForEvent.tsx textForCanonicalAliasEvent alt_aliases
  .filter(x => !arr.includes(x)) O(A*B) MEDIUM 19.5x — fix: Set.has()

element-web-0004: utils/arrays.ts arrayDiff + arrayIntersection
  .filter(i => !arr.includes(i)) O(A*B) MEDIUM 18x — fix: Set.has()
  Used by 15+ callsites: room-list, notifications, spaces, beacons, maps

conduit-0001: server_server.rs get_missing_events_route
  earliest_events.contains() (Vec) in growing BFS loop O(Q*E) MEDIUM-HIGH 7.4x
  Federation endpoint — remote server controls input size

matrix-rust-sdk: could not clone (GitHub HTTPS unreachable)
2026-03-30 15:16:54 -04:00

128 lines
5 KiB
Java
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.

import java.util.*;
/**
* CWE-407 unit tests for Conduit (Rust Matrix homeserver) defects.
*
* conduit-0001: server_server.rs get_missing_events_route()
* body.earliest_events.contains() (Vec) inside growing BFS while-loop
* O(Q*E) where Q = queued events, E = earliest events.
* Fix: convert earliest_events to HashSet for O(1) membership test.
* Severity: MEDIUM-HIGH (federation endpoint, remote server controls input)
*/
public class ConduitTest {
// --- conduit-0001: earliest_events Vec::contains in BFS loop ---
/**
* Simulates the defective path: for each queued event, do a linear scan
* of earliest_events (ArrayList.contains -> O(E)).
* As BFS expands queued_events, total work = O(Q * E).
*/
static int getMissingEventsVec(List<String> latestEvents, List<String> earliestEvents,
Map<String, List<String>> prevEventsMap, int limit) {
List<String> queued = new ArrayList<>(latestEvents);
List<String> result = new ArrayList<>();
int i = 0;
int ops = 0;
while (i < queued.size() && result.size() < limit) {
String eventId = queued.get(i);
// Linear scan of earliestEvents — the defect
ops++;
if (earliestEvents.contains(eventId)) { // O(E) each time
i++;
continue;
}
result.add(eventId);
// Expand BFS: add prev_events
List<String> prevEvents = prevEventsMap.getOrDefault(eventId, Collections.emptyList());
queued.addAll(prevEvents);
i++;
}
return ops;
}
/**
* Simulates the fixed path: convert earliest_events to HashSet once,
* then O(1) membership test per queued event.
*/
static int getMissingEventsSet(List<String> latestEvents, List<String> earliestEvents,
Map<String, List<String>> prevEventsMap, int limit) {
List<String> queued = new ArrayList<>(latestEvents);
Set<String> earliestSet = new HashSet<>(earliestEvents); // O(E) once
List<String> result = new ArrayList<>();
int i = 0;
int ops = 0;
while (i < queued.size() && result.size() < limit) {
String eventId = queued.get(i);
ops++;
if (earliestSet.contains(eventId)) { // O(1) each time
i++;
continue;
}
result.add(eventId);
List<String> prevEvents = prevEventsMap.getOrDefault(eventId, Collections.emptyList());
queued.addAll(prevEvents);
i++;
}
return ops;
}
static void testConduit0001() throws Exception {
// Build a synthetic DAG: E earliest events, chain of events leading back to them
int E = 500; // earliest events count
int depth = 50; // BFS depth
int limit = 10000;
List<String> earliestEvents = new ArrayList<>();
for (int i = 0; i < E; i++) {
earliestEvents.add("$earliest_" + i);
}
// Build a DAG where each event has 2 prev_events, creating a growing BFS frontier
Map<String, List<String>> prevEventsMap = new HashMap<>();
List<String> latestEvents = new ArrayList<>();
latestEvents.add("$latest_0");
// Create chain: latest -> intermediate events -> eventually hits earliest
String current = "$latest_0";
for (int d = 0; d < depth; d++) {
List<String> prevs = new ArrayList<>();
prevs.add("$event_" + d + "_a");
prevs.add("$event_" + d + "_b");
prevEventsMap.put(current, prevs);
current = "$event_" + d + "_a";
}
// Terminal events point to earliest events
List<String> terminalPrevs = new ArrayList<>();
terminalPrevs.add(earliestEvents.get(0));
terminalPrevs.add(earliestEvents.get(E / 2));
prevEventsMap.put(current, terminalPrevs);
// Correctness: both paths produce the same op count pattern
// (they process same events, just with different contains() cost)
// Performance
long t0 = System.nanoTime();
for (int r = 0; r < 500; r++) {
getMissingEventsVec(latestEvents, earliestEvents, prevEventsMap, limit);
}
long tVec = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < 500; r++) {
getMissingEventsSet(latestEvents, earliestEvents, prevEventsMap, limit);
}
long tSet = System.nanoTime() - t0;
double ratio = (double) tVec / tSet;
System.out.printf("conduit-0001: vec=%.3fs set=%.3fs ratio=%.1f× (E=%d, depth=%d)%n",
tVec / 1e9, tSet / 1e9, ratio, E, depth);
assert ratio > 2 : "Expected >2x speedup, got " + ratio;
System.out.println("PASS conduit-0001");
}
public static void main(String[] args) throws Exception {
testConduit0001();
System.out.println("ALL PASS");
}
}