Batch 6 (9): dolibarr, jitsi-videobridge, zed, tryton, suricata, strawberry, zulip, zesarux, zephyr Batch 7 (15): xonotic (4), xash3d (3), xenia, xtuple, zabbix (2), zathura, zebra, yabause, zephyr-0001 Batch 8 (15): woodpecker (2), wine (4), widelands (3), wesnoth (3), wekan (3) Mix of CWE-407 and CWE-312.
6.3 KiB
Dolibarr ERP/CRM — CWE-407 + CWE-312 Disclosure Brief
2026-04-13 · Patches available — awaiting upstream merge
Finding
Five defects in Dolibarr ERP/CRM across the file integrity checker, GDPR data policy cron, bookkeeping reversal system, email collector, and LDAP login module. Three CWE-407 algorithmic complexity defects and two CWE-312 cleartext credential logging defects. All patched. Patches ready for upstream review.
The Defects
dolibarr-0001 (PATCHED — HIGH): htdocs/blockedlog/admin/filecheck.php:409
// in_array() — O(N) linear scan over $file_list['insignature']
if (!in_array($tmprelativefilename, $file_list['insignature'])) {
$file_list['insignature'] grows with the number of tracked files (I). Each scanned file (S) triggers an O(I) linear scan via in_array(). Total cost: O(S*I). At S=10,000 scanned files and I=8,000 signature entries, this fires ~80,000,000 comparisons during a file integrity check.
dolibarr-0002 (PATCHED — MEDIUM): htdocs/datapolicy/class/datapolicycron.class.php:370
// in_array() — O(N) scan over growing $processedIds list
if (in_array($obj->rowid, $processedIds) || !method_exists($this, $handlerMethod)) {
$processedIds grows as records get processed. Each new record checks in_array() against all previously processed IDs. Total cost: O(N^2) where N = records processed per GDPR policy action.
dolibarr-0003 (PATCHED — MEDIUM): htdocs/accountancy/class/bookkeeping.class.php:3753
// in_array() — O(A) scan over $alreadyExtourneT
if (in_array($bookKeeping->piece_num, $alreadyExtourneT)) {
$alreadyExtourneT holds all previously reversed accounting entries. Each bookkeeping entry (P) triggers an O(A) linear scan. Total cost: O(P*A). Fires during accounting reversal operations at fiscal year boundaries.
dolibarr-0004 (PATCHED — HIGH, CWE-312): htdocs/admin/emailcollector_card.php:575
// IMAP password logged verbatim — unconditional
dol_syslog("imap_open connectstring=".$connectstringsource." login=".$object->login." password=".$object->password."...");
The IMAP email collector logs the mail account password in plaintext to dol_syslog() on every connection attempt. Not gated on any debug flag. Any log aggregator, syslog daemon, or shared hosting log directory exposes the credential.
dolibarr-0005 (PATCHED — MEDIUM, CWE-312): htdocs/core/login/functions_ldap.php:98
// LDAP admin searchPassword first 3 chars logged + printed to browser
dol_syslog("... Pass:".dol_trunc($ldap->searchPassword, 3));
print "DEBUG: ... Pass:".dol_trunc($ldap->searchPassword, 3)."<br>\n";
When $ldapdebug flag activates, the LDAP admin bind password prefix leaks to both server logs and browser output. The dol_trunc() call still exposes the first 3 characters, enough to narrow brute-force attacks.
Complexity Proof
dolibarr-0001: At S=10,000 scanned files, I=8,000 signatures:
- Defective: 10,000 * 8,000 = 80,000,000 comparisons
- Fixed: 10,000 * 1 = 10,000 lookups (array_flip + isset)
- 83.5x speedup measured.
dolibarr-0002: At N=1,000 records:
- Defective: 1 + 2 + ... + 1,000 = ~500,000 comparisons
- Fixed: 1,000 lookups (array_flip + isset)
- 23.8x speedup measured.
dolibarr-0003: At P=500 entries, A=400 reversed:
- Defective: 500 * 400 = 200,000 comparisons
- Fixed: 500 lookups (isset on flipped array)
- 39.9x speedup measured.
Impact
Dolibarr serves over 100,000 businesses worldwide as an open-source ERP/CRM. The file integrity checker (dolibarr-0001) runs during security audits and admin panel checks. The GDPR data policy cron (dolibarr-0002) runs on schedule for EU compliance. The accounting reversal (dolibarr-0003) fires at fiscal year boundaries when accountants process bulk reversals.
The credential logging defects (dolibarr-0004, dolibarr-0005) expose mail and LDAP passwords in production log files. dolibarr-0004 fires unconditionally on every IMAP connection. dolibarr-0005 fires when LDAP debug mode activates, which administrators enable during directory integration troubleshooting.
The Fix
dolibarr-0001: array_flip() the signature list once, then isset() for O(1) lookups:
// Before
if (!in_array($tmprelativefilename, $file_list['insignature'])) {
// After
$insignature_set = array_flip($file_list['insignature']);
if (!isset($insignature_set[$tmprelativefilename])) {
dolibarr-0002: array_flip() the processed IDs, maintain the flipped set alongside the list:
// Before
if (in_array($obj->rowid, $processedIds) || ...) {
// After
$processedSet = array_flip($processedIds);
if (isset($processedSet[$obj->rowid]) || ...) {
// ... on success:
$processedSet[$obj->rowid] = true;
dolibarr-0003: Same pattern, flipped array for O(1) membership:
$alreadyExtourneSet = array();
// ... populate alongside $alreadyExtourneT
if (isset($alreadyExtourneSet[$bookKeeping->piece_num])) {
dolibarr-0004: Replace password with literal ***:
dol_syslog("imap_open connectstring=".$connectstringsource." login=".$object->login." password=*** ...");
dolibarr-0005: Replace dol_trunc() with literal *** in both log and print:
dol_syslog("... Pass:***");
print "DEBUG: ... Pass:***<br>\n";
Patch
Fixes available:
defects/dolibarr/patch/dolibarr-0001-filecheck-insignature-in_array.patchdefects/dolibarr/patch/dolibarr-0002-datapolicycron-processedIds-in_array.patchdefects/dolibarr/patch/dolibarr-0003-bookkeeping-extourne-in_array.patchdefects/dolibarr/patch/dolibarr-0004-emailcollector-imap-password-logged.patchdefects/dolibarr/patch/dolibarr-0005-ldap-searchpassword-logged.patch
CWE-407 speedups: dolibarr-0001 83.5x, dolibarr-0002 23.8x, dolibarr-0003 39.9x. CWE-312 fixes eliminate credential exposure in logs.
What We Ask
Patches ready for review.
- Confirm receipt and assign a GitHub issue reference (Dolibarr/dolibarr).
- Assess severity — dolibarr-0004 logs IMAP passwords unconditionally; dolibarr-0001 fires on every file integrity check.
- Coordinate a disclosure date — we target 90 days from first contact.
- We will credit the Dolibarr team in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.