import java.util.*; /** * CWE-407 unit tests for SuiteCRM defects. * * suitecrm-0001: InboundEmail.php in_array($overview->imap_uid, $uids) * — O(I*U) email cache UID membership check * suitecrm-0002: jjwg_Maps/controller.php in_array($display['id'], $records) * — O(D*R) map marker record filter * suitecrm-0003: Email.php in_array(trim($match[0]), $knownEmails) * — O(N^2) email dedup accumulator * suitecrm-0004: SugarBean.php in_array($field, $all_fields) inside nested foreach * — O(S*F^2) subpanel union field dedup during subpanel list view */ public class SuiteCRMCWE407Test { // ---- suitecrm-0001: InboundEmail UID membership ---- /** Unpatched: in_array on $uids array — O(I*U) */ static int inboundEmailUnpatched(List insertUids, List cachedUids) { int updateCount = 0; for (String uid : insertUids) { if (cachedUids.contains(uid)) { // O(U) per email updateCount++; } } return updateCount; } /** Patched: hash set for O(1) lookup — O(I+U) */ static int inboundEmailPatched(List insertUids, List cachedUids) { Set uidSet = new HashSet<>(cachedUids); // O(U) once int updateCount = 0; for (String uid : insertUids) { if (uidSet.contains(uid)) { // O(1) per email updateCount++; } } return updateCount; } // ---- suitecrm-0002: jjwg_Maps record filter ---- /** Unpatched: in_array on $records — O(D*R) */ static int mapMarkersUnpatched(List displayIds, List records) { int markers = 0; for (String id : displayIds) { if (records.contains(id)) { // O(R) per display markers++; } } return markers; } /** Patched: array_flip for O(1) lookup — O(D+R) */ static int mapMarkersPatched(List displayIds, List records) { Set recordsSet = new HashSet<>(records); // O(R) once int markers = 0; for (String id : displayIds) { if (recordsSet.contains(id)) { // O(1) per display markers++; } } return markers; } // ---- suitecrm-0003: Email knownEmails dedup ---- /** Unpatched: in_array on growing knownEmails — O(N^2) */ static int emailDedupUnpatched(List addresses) { List knownEmails = new ArrayList<>(); int unique = 0; for (String addr : addresses) { if (!knownEmails.contains(addr)) { // O(N) scan knownEmails.add(addr); unique++; } } return unique; } /** Patched: hash set for O(1) lookup — O(N) total */ static int emailDedupPatched(List addresses) { Set knownEmailsSet = new HashSet<>(); int unique = 0; for (String addr : addresses) { if (!knownEmailsSet.contains(addr)) { // O(1) lookup knownEmailsSet.add(addr); unique++; } } return unique; } // ---- suitecrm-0004: SugarBean subpanel union field dedup ---- /** * Unpatched: in_array($field, $all_fields) inside nested foreach over subpanels * fields. * $all_fields grows each time a new unique field is appended. * Complexity: O(S * F * all_fields_size) = O(S * F^2) worst case. */ static List subpanelFieldDedupUnpatched(List> subpanelFields) { List allFields = new ArrayList<>(); for (List fields : subpanelFields) { for (String field : fields) { if (!allFields.contains(field)) { // O(allFields) scan allFields.add(field); } } } return allFields; } /** * Patched: isset($all_fields_set[$field]) — O(1) lookup per field. * Complexity: O(S * F) total. */ static List subpanelFieldDedupPatched(List> subpanelFields) { List allFields = new ArrayList<>(); Set allFieldsSet = new HashSet<>(); // O(1) lookup for (List fields : subpanelFields) { for (String field : fields) { if (!allFieldsSet.contains(field)) { // O(1) allFields.add(field); allFieldsSet.add(field); } } } return allFields; } // ---- Test harness ---- static void testSuiteCRM0001() { System.out.println("=== suitecrm-0001: InboundEmail UID membership ==="); int U = 10000; // cached UIDs in mailbox int I = 500; // new emails to insert List cachedUids = new ArrayList<>(U); for (int i = 0; i < U; i++) { cachedUids.add("uid_" + i); } List insertUids = new ArrayList<>(I); for (int i = U - I / 2; i < U + I / 2; i++) { insertUids.add("uid_" + i); } // Warmup inboundEmailPatched(insertUids, cachedUids); long t0 = System.nanoTime(); int r1 = inboundEmailUnpatched(insertUids, cachedUids); long unpatched = System.nanoTime() - t0; t0 = System.nanoTime(); int r2 = inboundEmailPatched(insertUids, cachedUids); long patched = System.nanoTime() - t0; assert r1 == r2 : "Result mismatch"; assert r1 == I / 2 : "Expected " + (I / 2) + " updates, got " + r1; double ratio = (double) unpatched / patched; System.out.printf(" U=%d I=%d unpatched=%dms patched=%dms ratio=%.1fx%n", U, I, unpatched / 1_000_000, patched / 1_000_000, ratio); assert ratio > 5.0 : "Expected >5x ratio, got " + ratio; System.out.println(" PASS"); } static void testSuiteCRM0002() { System.out.println("=== suitecrm-0002: jjwg_Maps record filter ==="); int D = 5000; // display results int R = 3000; // selected records List displayIds = new ArrayList<>(D); for (int i = 0; i < D; i++) { displayIds.add("id_" + i); } List records = new ArrayList<>(R); for (int i = 0; i < R; i++) { records.add("id_" + (i * 2)); // even IDs only } // Warmup mapMarkersPatched(displayIds, records); long t0 = System.nanoTime(); int r1 = mapMarkersUnpatched(displayIds, records); long unpatched = System.nanoTime() - t0; t0 = System.nanoTime(); int r2 = mapMarkersPatched(displayIds, records); long patched = System.nanoTime() - t0; assert r1 == r2 : "Result mismatch"; double ratio = (double) unpatched / patched; System.out.printf(" D=%d R=%d unpatched=%dms patched=%dms ratio=%.1fx%n", D, R, unpatched / 1_000_000, patched / 1_000_000, ratio); assert ratio > 5.0 : "Expected >5x ratio, got " + ratio; System.out.println(" PASS"); } static void testSuiteCRM0003() { System.out.println("=== suitecrm-0003: Email knownEmails dedup ==="); int N = 10000; // email addresses in mass send List addresses = new ArrayList<>(N); for (int i = 0; i < N; i++) { addresses.add("user" + i + "@example.com"); } // Add duplicates for (int i = 0; i < N / 5; i++) { addresses.add("user" + i + "@example.com"); } // Warmup emailDedupPatched(addresses); long t0 = System.nanoTime(); int r1 = emailDedupUnpatched(addresses); long unpatched = System.nanoTime() - t0; t0 = System.nanoTime(); int r2 = emailDedupPatched(addresses); long patched = System.nanoTime() - t0; assert r1 == r2 : "Result mismatch"; assert r1 == N : "Expected " + N + " unique, got " + r1; double ratio = (double) unpatched / patched; System.out.printf(" N=%d unpatched=%dms patched=%dms ratio=%.1fx%n", N, unpatched / 1_000_000, patched / 1_000_000, ratio); assert ratio > 5.0 : "Expected >5x ratio, got " + ratio; System.out.println(" PASS"); } static void testSuiteCRM0004() { System.out.println("=== suitecrm-0004: SugarBean subpanel field dedup ==="); int S = 40; // subpanels in a busy Account detail view int F = 60; // fields per subpanel query // Build subpanel field lists with overlapping fields across subpanels List> subpanelFields = new ArrayList<>(); for (int s = 0; s < S; s++) { List fields = new ArrayList<>(); for (int f = 0; f < F; f++) { // First 20 fields are shared (common: id, name, date_entered, etc.) if (f < 20) { fields.add("common_field_" + f); } else { fields.add("subpanel_" + s + "_field_" + f); } } subpanelFields.add(fields); } // Warmup subpanelFieldDedupPatched(subpanelFields); long t0 = System.nanoTime(); List r1 = subpanelFieldDedupUnpatched(subpanelFields); long unpatched = System.nanoTime() - t0; t0 = System.nanoTime(); List r2 = subpanelFieldDedupPatched(subpanelFields); long patched = System.nanoTime() - t0; assert r1.equals(r2) : "Result mismatch: " + r1.size() + " vs " + r2.size(); int expectedUnique = 20 + S * (F - 20); // 20 common + S*40 unique per subpanel assert r1.size() == expectedUnique : "Expected " + expectedUnique + " unique fields, got " + r1.size(); double ratio = (double) unpatched / patched; System.out.printf(" S=%d F=%d unique=%d unpatched=%dms patched=%dms ratio=%.1fx%n", S, F, r1.size(), unpatched / 1_000_000, patched / 1_000_000, ratio); assert ratio > 3.0 : "Expected >3x ratio, got " + ratio; System.out.println(" PASS"); } public static void main(String[] args) { testSuiteCRM0001(); testSuiteCRM0002(); testSuiteCRM0003(); testSuiteCRM0004(); System.out.println("\nAll 4 SuiteCRM CWE-407 tests PASSED."); } }