suitecrm+dolibarr: 5-MOAD scan — 3 new defects, MOADs 0002/0003/0005 CLEAN

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).
This commit is contained in:
russell@unturf.com 2026-03-31 20:27:34 -04:00
parent 14c3a92bab
commit fc7fdad2dc
9 changed files with 301 additions and 3 deletions

View file

@ -9,6 +9,8 @@ import java.util.*;
* 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 {
@ -90,6 +92,43 @@ public class SuiteCRMCWE407Test {
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() {
@ -195,10 +234,53 @@ public class SuiteCRMCWE407Test {
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();
System.out.println("\nAll 3 SuiteCRM CWE-407 tests PASSED.");
testSuiteCRM0004();
System.out.println("\nAll 4 SuiteCRM CWE-407 tests PASSED.");
}
}