suitecrm-0004: SugarBean.php subpanel union field dedup in_array($field, $all_fields) inside nested foreach over subpanel queries — O(S*F^2), 4.3x at S=40 F=60. Fix: parallel hash set for O(1) isset() check. dolibarr-0004: emailcollector_card.php line 575 — IMAP password logged verbatim unconditionally in dol_syslog(). CWE-312 HIGH. Fix: replace with literal ***. dolibarr-0005: functions_ldap.php line 98 — LDAP admin searchPassword first 3 chars leaked via dol_trunc() in dol_syslog() and browser print when $ldapdebug=true. CWE-312 MEDIUM. Fix: replace dol_trunc(...) with literal *** in both sinks. All 4 SuiteCRM tests PASS. All 5 Dolibarr tests PASS. MOADs 0002/0003/0005 CLEAN for both targets (PHP single-threaded, architectural globals).
286 lines
10 KiB
Java
286 lines
10 KiB
Java
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<String> insertUids, List<String> 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<String> insertUids, List<String> cachedUids) {
|
|
Set<String> 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<String> displayIds, List<String> 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<String> displayIds, List<String> records) {
|
|
Set<String> 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<String> addresses) {
|
|
List<String> 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<String> addresses) {
|
|
Set<String> 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<String> subpanelFieldDedupUnpatched(List<List<String>> subpanelFields) {
|
|
List<String> allFields = new ArrayList<>();
|
|
for (List<String> 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<String> subpanelFieldDedupPatched(List<List<String>> subpanelFields) {
|
|
List<String> allFields = new ArrayList<>();
|
|
Set<String> allFieldsSet = new HashSet<>(); // O(1) lookup
|
|
for (List<String> 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<String> cachedUids = new ArrayList<>(U);
|
|
for (int i = 0; i < U; i++) {
|
|
cachedUids.add("uid_" + i);
|
|
}
|
|
List<String> 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<String> displayIds = new ArrayList<>(D);
|
|
for (int i = 0; i < D; i++) {
|
|
displayIds.add("id_" + i);
|
|
}
|
|
List<String> 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<String> 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<List<String>> subpanelFields = new ArrayList<>();
|
|
for (int s = 0; s < S; s++) {
|
|
List<String> 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<String> r1 = subpanelFieldDedupUnpatched(subpanelFields);
|
|
long unpatched = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
List<String> 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.");
|
|
}
|
|
}
|