java-topology/defects/thunderbird-0003/test/ThunderbirdAutoSyncManagerTest.java
russell@unturf.com 6a025795dc thunderbird: 6 CWE-407 defects from 5-MOAD deep scan via Searchfox
thunderbird-0001: nsMsgAccountManager LoadAccounts IndexOf O(N^2) dedup MEDIUM 192x
thunderbird-0002: nsMsgCopyService DoNextCopy ContainsObject O(N^2) MEDIUM-HIGH 44x
thunderbird-0003: nsAutoSyncManager IndexOf in sync loops O(N^2) HIGH 751x
thunderbird-0004: nsImapFlagAndUidState Contains/IndexOf linear on sorted O(N) HIGH 107x
thunderbird-0005: nsMsgFilterList ComputeArbitraryHeaders FindInReadable O(H^2) MEDIUM 64x
thunderbird-0006: nsSpamSettings CheckWhiteList linear email scan O(M*E) MEDIUM 153x

MOAD-0002 (Intertangle): MEDIUM - singleton nsMsgAccountManager shared across protocols
MOAD-0003 (Leaked Context): LOW-MEDIUM - IMAP connection pool single auth boolean
MOAD-0004 (Logged Secret): MEDIUM - debug builds log credentials via MOZ_UPDATE_CHANNEL bypass
MOAD-0005 (CWE-362): LOW-MEDIUM - IMAP folder DB init and connection pool TOCTOU

12/12 unit tests PASS
2026-03-30 17:33:16 -04:00

124 lines
4.8 KiB
Java

import java.util.*;
/**
* Unit test for thunderbird-0003: nsAutoSyncManager IndexOf() in loops O(N^2)
*
* Simulates the IMAP auto-sync folder queue management where IndexOf() is
* called per folder to find queue positions during sync scheduling.
*
* Defective: O(N^2) - IndexOf per folder in update loop
* Fixed: O(N) - HashMap for O(1) index lookups
*/
public class ThunderbirdAutoSyncManagerTest {
// --- Defective: O(N^2) index lookup per folder ---
static int autoUpdateFoldersDefective(List<String> folders, List<String> updateQ) {
int updates = 0;
for (String folder : folders) {
int idx = updateQ.indexOf(folder); // O(N) linear scan
if (idx >= 0) {
updates++;
// Simulate state check using index position
}
}
return updates;
}
// --- Fixed: O(N) with pre-built index map ---
static int autoUpdateFoldersFixed(List<String> folders, Map<String, Integer> updateQIndex) {
int updates = 0;
for (String folder : folders) {
Integer idx = updateQIndex.get(folder); // O(1) hash lookup
if (idx != null) {
updates++;
}
}
return updates;
}
// --- Defective: O(N^2) sibling chaining ---
static List<String> chainFoldersDefective(List<String> queue, List<String> priorityQ) {
List<String> chained = new ArrayList<>();
chained.add(priorityQ.get(0));
for (int pqIdx = 1; pqIdx < priorityQ.size(); pqIdx++) {
String candidate = priorityQ.get(pqIdx);
for (int idx = 0; idx < chained.size(); idx++) {
// Simulate IsSibling check (same server prefix)
if (candidate.startsWith(chained.get(idx).split("/")[0])) {
chained.add(candidate);
break;
}
}
}
return chained;
}
// --- Fixed: O(N) sibling chaining with server set ---
static List<String> chainFoldersFixed(List<String> queue, List<String> priorityQ) {
List<String> chained = new ArrayList<>();
Set<String> serverSet = new HashSet<>();
chained.add(priorityQ.get(0));
serverSet.add(priorityQ.get(0).split("/")[0]);
for (int pqIdx = 1; pqIdx < priorityQ.size(); pqIdx++) {
String candidate = priorityQ.get(pqIdx);
String server = candidate.split("/")[0];
if (serverSet.contains(server)) {
chained.add(candidate);
}
}
return chained;
}
public static void main(String[] args) {
System.out.println("=== thunderbird-0003: nsAutoSyncManager IndexOf O(N^2) ===\n");
// Correctness for autoUpdateFolders
List<String> folders = Arrays.asList("f1", "f2", "f3", "f4");
List<String> updateQ = Arrays.asList("f2", "f4", "f6");
Map<String, Integer> updateQIndex = new HashMap<>();
for (int i = 0; i < updateQ.size(); i++) updateQIndex.put(updateQ.get(i), i);
int defCount = autoUpdateFoldersDefective(folders, updateQ);
int fixCount = autoUpdateFoldersFixed(folders, updateQIndex);
assert defCount == fixCount : "Counts must match";
assert defCount == 2;
System.out.println("PASS correctness: autoUpdate matches = " + defCount);
// Benchmark autoUpdateFolders
int[] sizes = {100, 500, 1000, 5000};
for (int N : sizes) {
List<String> allFolders = new ArrayList<>();
List<String> uQ = new ArrayList<>();
Map<String, Integer> uQIndex = new HashMap<>();
for (int i = 0; i < N; i++) {
allFolders.add("folder" + i);
if (i % 3 == 0) { // ~33% in update queue
uQ.add("folder" + i);
uQIndex.put("folder" + i, uQ.size() - 1);
}
}
// Warmup
for (int w = 0; w < 3; w++) {
autoUpdateFoldersDefective(allFolders, uQ);
autoUpdateFoldersFixed(allFolders, uQIndex);
}
int iters = Math.max(1, 50000 / N);
long t0 = System.nanoTime();
for (int r = 0; r < iters; r++) autoUpdateFoldersDefective(allFolders, uQ);
long defTime = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < iters; r++) autoUpdateFoldersFixed(allFolders, uQIndex);
long fixTime = System.nanoTime() - t0;
double ratio = (double) defTime / Math.max(1, fixTime);
String status = ratio >= 2.0 ? "PASS" : "FAIL";
System.out.printf("%s N=%5d defective=%8dns fixed=%8dns ratio=%.1fx%n",
status, N, defTime / iters, fixTime / iters, ratio);
}
System.out.println("\nAll tests passed.");
}
}