Community Builder 2.11.0 — Security Patches (Proposes)All patches refer to the extracted files from:
Code:
pkg_communitybuilder_2_11_0_build_2026_05_04_17_01_28_8d34d7a6c.zip
Paths are relative to the inner package:
Code:
001_pkg_communitybuilder_2.11.0+…zip
PATCH 1 — Remove eval() (HIGH)
File:
Code:
packages/lib_CBLib.zip
→
Code:
CB/Legacy/LegacyComprofilerFunctions.php
, line 2207Problem
Code:
// BEFORE (unsafe)
function cbDeleteUser ( $id, $condition = null, $inComprofilerOnly = false ) {
// …
if ( ( $condition == null ) || eval( $condition ) ) {
if ( ! $user->delete( (int) $id, $inComprofilerOnly ) ) {
return $user->getError();
}
return '';
}
return false;
}
is executed as PHP code. Any plugin passing an attacker-controlled string into this function could achieve Remote Code Execution.FixThe only known caller (CBSubs 3.0.0) exclusively passes:
Code:
"return ($user->block == 1);"
This can be implemented safely without
.
Code:
// AFTER (safe)
/**
* @deprecated 2.0 Use UserTable()->load($id)->delete(null, $cbUserOnly)
*
* @param int $id
* @param string|null $condition Only accepted value: "block" (checks $user->block == 1).
* All other values are ignored and deletion proceeds
* as if $condition === null.
* The eval()-path has been removed for security reasons.
* @param bool $inComprofilerOnly
* @return null|bool|string
*/
function cbDeleteUser( $id, $condition = null, $inComprofilerOnly = false ) {
if ( ! $id ) {
return null;
}
$user = new UserTable();
if ( $inComprofilerOnly ) {
$user->load( array( 'user_id' => (int) $id ) );
} else {
$user->load( (int) $id );
}
if ( ! $user->id ) {
return null;
}
// Replacement for the old eval()-path:
$conditionMet = true;
if ( $condition !== null ) {
if ( $condition === 'block' || strpos( $condition, '$user->block' ) !== false ) {
$conditionMet = ( (int) $user->block === 1 );
}
// All other condition strings are no longer evaluated as PHP.
// Plugins should migrate to UserTable()->load($id)->delete().
}
if ( $conditionMet ) {
if ( ! $user->delete( (int) $id, $inComprofilerOnly ) ) {
return $user->getError();
}
return '';
}
return false;
}
Note for plugin developers:
If a plugin currently calls:
Code:
cbDeleteUser( $id, $myConditionString )
and the string is not:
Code:
"return ($user->block == 1);"
it should migrate to:
Code:
UserTable()->load($id)->delete()
PATCH 2 — Replace MD5 Tokens with HMAC-SHA256 (HIGH)
Files:
-
Code:
packages/com_comprofiler.zip
→
Code:
site/comprofiler.html.php
, line 60
-
Code:
packages/com_comprofiler.zip
→
Code:
site/comprofiler.php
, line 353
Problem
Code:
// BEFORE — comprofiler.html.php:60 (Token generation)
$salt = cbMakeRandomString( 16 );
$key = 'cbmv1_' . md5( $salt . $rowTo->id . $rowTo->password
. $rowTo->lastvisitDate . $rowFrom->password
. $rowFrom->lastvisitDate ) . '_' . $salt;
Code:
// BEFORE — comprofiler.php:353 (Token validation)
if ( ( count( $parts ) == 3 )
&& ( $parts[0] == 'cbmv1' )
&& ( strlen( $parts[2] ) == 16 )
&& ( $parts[1] == md5( $parts[2] . $rowTo->id . $rowTo->password
. $rowTo->lastvisitDate . $rowFrom->password
. $rowFrom->lastvisitDate ) ) ) {
MD5 is collision-prone and unsuitable for security-sensitive token generation.Fix
Code:
// AFTER — comprofiler.html.php:60 (Token generation)
$salt = bin2hex( random_bytes( 16 ) );
$hmac = hash_hmac(
'sha256',
$salt . '|' . $rowTo->id . '|' . $rowTo->password
. '|' . $rowTo->lastvisitDate
. '|' . $rowFrom->password
. '|' . $rowFrom->lastvisitDate,
$_CB_framework->getCfg( 'secret' )
);
$key = 'cbmv2_' . $hmac . '_' . $salt;
Code:
// AFTER — comprofiler.php:353 (Token validation)
if ( ( count( $parts ) == 3 ) && ( $parts[0] === 'cbmv2' ) ) {
$salt = $parts[2];
$expectedHmac = hash_hmac(
'sha256',
$salt . '|' . $rowTo->id . '|' . $rowTo->password
. '|' . $rowTo->lastvisitDate
. '|' . $rowFrom->password
. '|' . $rowFrom->lastvisitDate,
$_CB_framework->getCfg( 'secret' )
);
$tokenValid = hash_equals( $expectedHmac, $parts[1] );
} elseif ( ( count( $parts ) == 3 ) && ( $parts[0] === 'cbmv1' ) ) {
// Legacy compatibility path
$tokenValid = ( strlen( $parts[2] ) == 16 )
&& ( $parts[1] === md5( $parts[2] . $rowTo->id . $rowTo->password
. $rowTo->lastvisitDate . $rowFrom->password
. $rowFrom->lastvisitDate ) );
} else {
$tokenValid = false;
}
if ( $tokenValid ) {
// Existing logic unchanged
}
prevents timing attacks during token comparison.
PATCH 3 — Enable IP Anonymization by Default (HIGH / GDPR)
File:
Code:
packages/com_comprofiler.zip
→
Code:
install.comprofiler.php
Problem
Code:
anonymize_ip_addresses
defaults to
.Complete IPv4 and IPv6 addresses are permanently stored in:
-
Code:
#__comprofiler_userreports
-
Code:
#__comprofiler_userviews
This may violate GDPR requirements for EU installations.Fix A — Installer Default
Code:
if ( ! $existingInstall ) {
Application::Config()->set( 'anonymize_ip_addresses', true );
Application::Config()->set( 'profile_views_log', false );
Application::Config()->save();
}
Fix B — Existing Installations
Code:
UPDATE `#__comprofiler_userviews`
SET `lastip` = CONCAT(
SUBSTRING_INDEX(`lastip`, '.', 3),
'.0'
)
WHERE `lastip` REGEXP '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$';
Code:
UPDATE `#__comprofiler_userreports`
SET `ip` = CONCAT(
SUBSTRING_INDEX(`ip`, '.', 3),
'.0'
)
WHERE `ip` REGEXP '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$';
GDPR note:
IP truncation reduces identifiability and supports compliance with GDPR data minimization principles.
PATCH 4 — Cascade User Deletion (MEDIUM / GDPR Art. 17)
File:
Code:
packages/com_comprofiler.zip
→
Code:
admin/controller/controller.default.php
ProblemWhen deleting a user, IP logs and profile-view records remain in the database.FixAfter successful
execution:
Code:
$_CB_database->setQuery(
'DELETE FROM ' . $_CB_database->NameQuote( '#__comprofiler_userviews' )
. ' WHERE ' . $_CB_database->NameQuote( 'viewer_id' ) . ' IN (' . $idList . ')'
. ' OR ' . $_CB_database->NameQuote( 'profile_id' ) . ' IN (' . $idList . ')'
);
$_CB_database->query();
And similarly for:
-
Code:
#__comprofiler_userreports
- custom field values
This supports GDPR “Right to Erasure” compliance.PATCH 5 — Clickjacking Protection via HTTP Headers (LOW)Recommended Joomla Plugin
Code:
public function onAfterDispatch(): void
{
$app = Factory::getApplication();
if ( $app->isClient( 'administrator' ) ) {
$response = $app->getResponse();
$response->setHeader( 'X-Frame-Options', 'SAMEORIGIN', true );
$response->setHeader( 'Content-Security-Policy', "frame-ancestors 'self'", true );
$response->setHeader( 'X-Content-Type-Options', 'nosniff', true );
$response->setHeader( 'Referrer-Policy', 'strict-origin-when-cross-origin', true );
}
}
PATCH 6 — Replace cbMakeRandomString() with random_bytes() (LOW)
File:
Code:
packages/lib_CBLib.zip
→
Code:
CB/Legacy/LegacyComprofilerFunctions.php
, line 1844Problem
Code:
cbMakeRandomString()
relies on
which is predictable.Fix
Code:
function cbMakeRandomString( $stringLength = 8, $noCaps = false ) {
if ( $noCaps ) {
$chars = 'abchefghjkmnpqrstuvwxyz0123456789';
} else {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
}
$len = strlen( $chars );
$rndString = '';
$bytes = random_bytes( $stringLength );
for ( $i = 0; $i < $stringLength; $i++ ) {
$rndString .= $chars[ ord( $bytes[$i] ) % $len ];
}
return $rndString;
}
Recommended Privacy ConfigurationSettingRecommended ValueReason
Code:
anonymize_ip_addresses
GDPR Art. 5(1)(c)
GDPR Art. 7
Data minimizationJoomla Privacy Consent PluginenabledNative Joomla consentPriority Summary#PatchEffortPriority1Remove eval()lowimmediate2MD5 → HMAC-SHA256mediumimmediate3Enable IP anonymizationlowimmediate4Cascade deletionmediumnext release5HTTP security headerslowimmediate6random_bytes()lownext releaseAdditional note:To better understand and document the topics above, we have now started preparing concrete proof-of-concept patches and hardening proposals based on our internal review.The intention is explicitly constructive:
We are not trying to criticize Community Builder, but rather evaluate whether it could become a long-term strategic foundation for larger Joomla-based community projects with strong privacy and ACL requirements.The areas we are currently reviewing include:
– removal of legacy
paths
– migration from MD5-based tokens to modern HMAC-SHA256 approaches
– GDPR-oriented defaults (especially IP anonymization)
– stronger deletion cascades for privacy compliance
– modern HTTP security headers
– replacement of older random generators with
Some of these are probably legacy compatibility decisions rather than active security oversights, which is fully understandable in a mature project with a long history.If useful for the developers or the community, we would also be happy to share:
– patch proposals
– code diffs
– implementation ideas
– GDPR-oriented configuration recommendationsAgain: overall our impression of Community Builder is positive. The Joomla ACL integration and architectural flexibility are very attractive for serious community-driven platforms. We simply want to understand how actively these topics are already being discussed internally and whether there are existing roadmaps or considerations around them.Thanks again for your work and for any insight.