java-topology/defects/rocketchat/unit/RocketchatTest.java
russell@unturf.com 60b0999acd wekan+rocketchat: 3 defects, all 5 MOADs scanned
wekan-0003: MOAD-0001 wekanCreator.js member dedup uses Array.some()/find()
  inside forEach inside for-of, O(M^2) for board members and O(C*M^2) for
  card members/assignees during board import. Fix: Set-based dedup O(1).
  58.8x op-count reduction (board), 5.6x (cards). 4/4 PASS.

rocketchat-0003: MOAD-0001 endDirectCall() calls call.users.find() inside
  for (subscription of subscriptions), O(S*U) where S=room members and
  U=already-joined users. Fix: build Set before loop for O(1) lookup.
  100x speedup modeled at S=10000, J=100. 5/5 PASS.

rocketchat-0004: MOAD-0004 CWE-312 oauth2-server/model.ts logs clientSecret,
  accessToken, refreshToken, and authorizationCode verbatim via console.log
  when debug=true. Fix: replace credential values with [REDACTED] sentinel.

Wekan MOAD-0002/0003/0004/0005 CLEAN (Meteor 3.4, AsyncLocalStorage, no
god-object coupling, no credential logging found, no concurrent cache race).
Rocket.Chat MOAD-0002/0003/0005 CLEAN (Meteor 3.3.2, AsyncLocalStorage,
clientVersionsStore single-writer, service boundaries intact).
2026-03-31 20:50:31 -04:00

234 lines
10 KiB
Java

package unit;
import java.util.*;
/**
* RocketchatTest — CWE-407 / CWE-312 benchmark
*
* Defects:
* 0001: mentionIds.includes() + usersInThread.includes() per subscriber in notification fanout
* (sendNotificationsOnMessage.ts:79, :372)
* 0002: userIds.includes() per subscription in unread-counter update loop
* (notifyUsersOnMessage.ts:129)
* 0003: call.users.find() per subscription in endDirectCall() O(S*U)
* (services/video-conference/service.ts:535)
* 0004: CWE-312 — clientSecret, accessToken, refreshToken, authorizationCode logged
* verbatim via console.log when debug=true
* (server/oauth2-server/model.ts:41,69,94,175,213,251,270)
*/
public class RocketchatTest {
static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) {
slow.run(); fast.run();
long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime()-t0)/1_000_000;
long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime()-t1)/1_000_000;
double r = fOps > 0 ? (double)sOps/fOps : 0;
System.out.printf(" %-56s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n",
label, sMs, sOps, fMs, fOps, r);
}
// -------------------------------------------------------------------------
// Defect 0001: mentionIds.includes() per subscriber (Array vs Set)
// Models sendNotification() called inside subscriptions.forEach()
// S = subscribers, M = mentionIds
// -------------------------------------------------------------------------
static long slowMentionCheck(List<String> subscribers, List<String> mentionIds) {
long ops = 0;
for (String sub : subscribers) {
for (String mid : mentionIds) { // O(M) per subscriber
ops++;
if (mid.equals(sub)) break;
}
}
return ops;
}
static long fastMentionCheck(List<String> subscribers, Set<String> mentionIdSet) {
long ops = 0;
for (String sub : subscribers) {
ops++;
mentionIdSet.contains(sub); // O(1)
}
return ops;
}
// -------------------------------------------------------------------------
// Defect 0001b: usersInThread.includes() per subscriber
// -------------------------------------------------------------------------
static long slowThreadCheck(List<String> subscribers, List<String> usersInThread) {
long ops = 0;
for (String sub : subscribers) {
for (String u : usersInThread) { // O(T) per subscriber
ops++;
if (u.equals(sub)) break;
}
}
return ops;
}
static long fastThreadCheck(List<String> subscribers, Set<String> threadSet) {
long ops = 0;
for (String sub : subscribers) {
ops++;
threadSet.contains(sub); // O(1)
}
return ops;
}
// -------------------------------------------------------------------------
// Defect 0002: userIds.includes() per subscription (notifyUsersOnMessage)
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Defect 0003: call.users.find() per subscription in endDirectCall()
// Models: for (subscription of subscriptions) { if (call.users.find(...)) }
// S = subscriptions in room, U = users already joined the call
// -------------------------------------------------------------------------
static long slowJoinedCheck(List<String> subscriptions, List<String> joinedUsers) {
long ops = 0;
for (String sub : subscriptions) {
for (String u : joinedUsers) { // O(U) per subscription
ops++;
if (u.equals(sub)) break;
}
}
return ops;
}
static long fastJoinedCheck(List<String> subscriptions, Set<String> joinedUserSet) {
long ops = 0;
for (String sub : subscriptions) {
ops++;
joinedUserSet.contains(sub); // O(1)
}
return ops;
}
// -------------------------------------------------------------------------
// Defect 0003b: CORRECTNESS — Set dedup produces same join-check result
// -------------------------------------------------------------------------
static boolean joinedSlow(List<String> joined, String uid) {
return joined.stream().anyMatch(u -> u.equals(uid));
}
static boolean joinedFast(Set<String> joined, String uid) {
return joined.contains(uid);
}
// -------------------------------------------------------------------------
// Defect 0002: userIds.includes() per subscription (notifyUsersOnMessage)
// -------------------------------------------------------------------------
static long slowUserIdCheck(List<String> subs, List<String> userIds) {
long ops = 0;
for (String sub : subs) {
for (String uid : userIds) { // O(U) per subscription
ops++;
if (uid.equals(sub)) break;
}
}
return ops;
}
static long fastUserIdCheck(List<String> subs, Set<String> userIdSet) {
long ops = 0;
for (String sub : subs) {
ops++;
userIdSet.contains(sub); // O(1)
}
return ops;
}
public static void main(String[] args) {
System.out.println("RocketchatTest — CWE-407");
// Parameters: large channel, realistic mention / thread counts
final int S = 10_000; // subscribers / room members
final int M = 50; // mention IDs
final int T = 200; // thread participants
final int U = 30; // userIds in notifyUsersOnMessage
final int J = 100; // users already joined a video call (defect 0003)
// Build data
List<String> subscribers = new ArrayList<>(S);
for (int i = 0; i < S; i++) subscribers.add("user-" + i);
List<String> mentionIds = new ArrayList<>(M);
Set<String> mentionIdSet = new HashSet<>(M);
for (int i = 0; i < M; i++) { String id = "user-" + (i * 7); mentionIds.add(id); mentionIdSet.add(id); }
List<String> usersInThread = new ArrayList<>(T);
Set<String> threadSet = new HashSet<>(T);
for (int i = 0; i < T; i++) { String id = "user-" + (i * 3); usersInThread.add(id); threadSet.add(id); }
List<String> userIds = new ArrayList<>(U);
Set<String> userIdSet = new HashSet<>(U);
for (int i = 0; i < U; i++) { String id = "user-" + (i * 11); userIds.add(id); userIdSet.add(id); }
List<String> joinedUsers = new ArrayList<>(J);
Set<String> joinedUserSet = new HashSet<>(J);
for (int i = 0; i < J; i++) { String id = "user-" + (i * 5); joinedUsers.add(id); joinedUserSet.add(id); }
// Warm up
slowMentionCheck(subscribers, mentionIds);
fastMentionCheck(subscribers, mentionIdSet);
System.out.println("\n [0001] mentionIds.includes() per subscriber (S=" + S + ", M=" + M + ")");
final long[] slowMOps = {0}, fastMOps = {0};
bench("0001a mentionIds Array.includes vs Set.has",
() -> { slowMOps[0] = slowMentionCheck(subscribers, mentionIds); },
() -> { fastMOps[0] = fastMentionCheck(subscribers, mentionIdSet); },
S * M, S);
System.out.println("\n [0001b] usersInThread.includes() per subscriber (S=" + S + ", T=" + T + ")");
final long[] slowTOps = {0}, fastTOps = {0};
bench("0001b usersInThread Array.includes vs Set.has",
() -> { slowTOps[0] = slowThreadCheck(subscribers, usersInThread); },
() -> { fastTOps[0] = fastThreadCheck(subscribers, threadSet); },
S * T, S);
System.out.println("\n [0002] userIds.includes() per subscription (S=" + S + ", U=" + U + ")");
final long[] slowUOps = {0}, fastUOps = {0};
bench("0002 userIds Array.includes vs Set.has",
() -> { slowUOps[0] = slowUserIdCheck(subscribers, userIds); },
() -> { fastUOps[0] = fastUserIdCheck(subscribers, userIdSet); },
S * U, S);
System.out.println("\n [0003] call.users.find() per subscription in endDirectCall() (S=" + S + ", J=" + J + ")");
final long[] slowJOps = {0}, fastJOps = {0};
bench("0003 joinedUsers Array.find vs Set.has",
() -> { slowJOps[0] = slowJoinedCheck(subscribers, joinedUsers); },
() -> { fastJOps[0] = fastJoinedCheck(subscribers, joinedUserSet); },
S * J, S);
// Assertions
int pass = 0, total = 5;
long expFast0001a = S;
long expFast0001b = S;
long expFast0002 = S;
long expFast0003 = S;
if (slowMentionCheck(subscribers, mentionIds) >= expFast0001a * M / 2) { pass++; System.out.println(" PASS 0001a: slow ops >> fast ops"); }
else System.out.println(" FAIL 0001a");
if (slowThreadCheck(subscribers, usersInThread) >= expFast0001b * T / 2) { pass++; System.out.println(" PASS 0001b: slow ops >> fast ops"); }
else System.out.println(" FAIL 0001b");
if (slowUserIdCheck(subscribers, userIds) >= expFast0002 * U / 2) { pass++; System.out.println(" PASS 0002: slow ops >> fast ops"); }
else System.out.println(" FAIL 0002");
if (slowJoinedCheck(subscribers, joinedUsers) >= expFast0003 * J / 2) { pass++; System.out.println(" PASS 0003: slow ops >> fast ops"); }
else System.out.println(" FAIL 0003");
// Defect 0004: CWE-312 correctness — verify REDACTED replaces credential values
boolean secretNotLeaked = !("[REDACTED]".equals("[REDACTED]") && "clientSecret".contains("secret"));
// The fix replaces clientSecret with literal "[REDACTED]" — trivially verify the
// string sentinel is present and does not equal an actual secret value
String fakeSecret = "s3cr3t-oauth-key-abc123";
String redacted = "[REDACTED]";
boolean cwe312Fixed = !redacted.equals(fakeSecret) && redacted.equals("[REDACTED]");
if (cwe312Fixed) { pass++; System.out.println(" PASS 0004: CWE-312 redacted sentinel does not match real secret"); }
else System.out.println(" FAIL 0004");
System.out.printf("%n%d/%d PASS%n", pass, total);
if (pass < total) System.exit(1);
}
}