Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/popup-by-supsystic/classes/frame.php
+++ b/popup-by-supsystic/classes/frame.php
@@ -164,6 +164,39 @@
}
}
/**
+ * Union a controller's own permission map with the base controller's map.
+ * A plain array_merge() here is wrong: both maps use the same top-level
+ * keys (PPS_METHODS / PPS_USERLEVELS), so array_merge() would let one
+ * wholesale replace the other instead of combining their method lists -
+ * silently dropping restrictions the controller itself defined (e.g. this
+ * previously let the popup module's admin-only 'save' restriction be
+ * overwritten away by the base controller's smaller default list).
+ * @param array $permissions Controller's own permissions
+ * @param array $permissionsBase Base controller's permissions
+ * @return array merged permissions map
+ */
+ private function _mergePermissions($permissions, $permissionsBase)
+ {
+ foreach ([PPS_METHODS, PPS_USERLEVELS] as $permKey) {
+ if (empty($permissionsBase[$permKey])) {
+ continue;
+ }
+ if (!isset($permissions[$permKey])) {
+ $permissions[$permKey] = [];
+ }
+ foreach ($permissionsBase[$permKey] as $userlevel => $methods) {
+ $incoming = is_array($methods) ? $methods : [$methods];
+ if (isset($permissions[$permKey][$userlevel])) {
+ $existing = is_array($permissions[$permKey][$userlevel]) ? $permissions[$permKey][$userlevel] : [$permissions[$permKey][$userlevel]];
+ $permissions[$permKey][$userlevel] = array_unique(array_merge($existing, $incoming));
+ } else {
+ $permissions[$permKey][$userlevel] = $incoming;
+ }
+ }
+ }
+ return $permissions;
+ }
+ /**
* Check permissions for action in controller by $code
* @param string $code Code of controller that need to be checked
* @param string $action Action that need to be checked
@@ -177,7 +210,7 @@
if ($mod) {
$permissions = $mod->getController()->getPermissions();
$permissionsBase = $mod->getController()->getBasePermissions();
- $permissions = array_merge($permissions, $permissionsBase);
+ $permissions = $this->_mergePermissions($permissions, $permissionsBase);
if (!empty($permissions)) {
// Special permissions
if (isset($permissions[PPS_METHODS]) && !empty($permissions[PPS_METHODS])) {
--- a/popup-by-supsystic/classes/installer.php
+++ b/popup-by-supsystic/classes/installer.php
@@ -556,7 +556,6 @@
$lastAutoIncrement = dbPps::getAutoIncrement('@__popup');
$lastAutoIncrement = $lastAutoIncrement && $lastAutoIncrement > 30 ? $lastAutoIncrement : 100;
$popupLastId = 39;
- //dbPps::query('DELETE FROM @__popup WHERE id IN ('. implode(',', range(0, $popupLastId)). ')'); // We updated all popups as we did changes in all of them
if (!dbPps::exist('@__popup', 'id', '7')) {
// First set of additional templates
dbPps::query('INSERT INTO @__popup (id,label,active,original_id,params,html,css,img_preview,show_on,show_to,show_pages,type_id,date_created,sort_order) VALUES
--- a/popup-by-supsystic/config.php
+++ b/popup-by-supsystic/config.php
@@ -45,7 +45,7 @@
define('PPS_CURRENT', 'current');
define('PPS_EOL', "n");
define('PPS_PLUGIN_INSTALLED', true);
-define('PPS_VERSION', '1.12.0');
+define('PPS_VERSION', '1.13.0');
define('PPS_USER', 'user');
define('PPS_CLASS_PREFIX', 'ppsc');
define('PPS_FREE_VERSION', false);
--- a/popup-by-supsystic/modules/popup/views/popup.php
+++ b/popup-by-supsystic/modules/popup/views/popup.php
@@ -353,7 +353,6 @@
}
private function _initBigDataStats()
{
- // $canSend = (int) framePps::_()->getModule('options')->get('send_stats');
// if( $canSend ) {
// framePps::_()->getModule('supsystic_promo')->connectItemEditStats();
// }
--- a/popup-by-supsystic/modules/subscribe/models/subscribe.php
+++ b/popup-by-supsystic/modules/subscribe/models/subscribe.php
@@ -354,8 +354,16 @@
// If there was selected some special role - check it here
$this->_lastPopup = $popup;
if (isset($popup['params']['tpl'][$pref . '_wp_create_user_role']) && !empty($popup['params']['tpl'][$pref . '_wp_create_user_role']) && $popup['params']['tpl'][$pref . '_wp_create_user_role'] != 'subscriber') {
- $user = new WP_User($userId);
- $user->set_role($popup['params']['tpl'][$pref . '_wp_create_user_role']);
+ $requestedRole = $popup['params']['tpl'][$pref . '_wp_create_user_role'];
+ // Never trust a role coming from stored popup config - only allow roles
+ // that are also offered in the admin UI's own role picker, which already
+ // excludes 'administrator' and 'editor' (see getAvailableUserRolesForSelect()).
+ $subscribeMod = framePps::_()->getModule('subscribe');
+ $allowedRoles = $subscribeMod ? $subscribeMod->getAvailableUserRolesForSelect() : [];
+ if (isset($allowedRoles[$requestedRole])) {
+ $user = new WP_User($userId);
+ $user->set_role($requestedRole);
+ }
}
if (isset($popup['params']['tpl'][$pref . '_fields']) && !empty($popup['params']['tpl'][$pref . '_fields'])) {
foreach ($popup['params']['tpl'][$pref . '_fields'] as $k => $f) {
@@ -437,7 +445,11 @@
$pref = $forReg ? 'reg' : 'sub';
$blogName = wp_specialchars_decode(get_bloginfo('name'));
$blogName = str_replace(''', "'", $blogName);
- $confirmLinkData = ['email' => $email, 'hash' => $confirmHash, '_wpnonce' => wp_create_nonce('pps_nonce')];
+ // Use a dedicated nonce action tied to this specific subscriber/hash rather
+ // than the generic 'pps_nonce' action used to gate admin-only AJAX actions -
+ // this link is emailed to the (unauthenticated) subscriber, so it must never
+ // double as a valid nonce for anything else.
+ $confirmLinkData = ['email' => $email, 'hash' => $confirmHash, '_wpnonce' => wp_create_nonce('pps_subscribe_confirm_' . $confirmHash)];
if ($forReg) {
$confirmLinkData['for_reg'] = 1;
}
--- a/popup-by-supsystic/modules/supsystic_promo/models/supsystic_promo.php
+++ b/popup-by-supsystic/modules/supsystic_promo/models/supsystic_promo.php
@@ -26,15 +26,7 @@
// In any case - give user posibility to move futher
return true;
}
- public function saveUsageStat($code, $unique = false)
- {
- // if($unique && $this->_checkUniqueStat($code)) {
- // return;
- // }
- // $query = 'INSERT INTO @__usage_stat SET code = "'. dbPps::escape($code). '", visits = 1
- // ON DUPLICATE KEY UPDATE visits = visits + 1';
- // return dbPps::query($query);
- }
+ public function saveUsageStat($code, $unique = false) {}
private function _checkUniqueStat($code)
{
// $uniqueStats = get_option(PPS_CODE. '_unique_stats');
@@ -47,17 +39,8 @@
// update_option(PPS_CODE. '_unique_stats', $uniqueStats);
// return true;
}
- public function saveSpentTime($code, $spent)
- {
- // $spent = (int) $spent;
- // $query = 'UPDATE @__usage_stat SET spent_time = spent_time + '. $spent. ' WHERE code = "'. $code. '"';
- // return dbPps::query($query);
- }
- public function getAllUsageStat()
- {
- // $query = 'SELECT * FROM @__usage_stat';
- // return dbPps::get($query);
- }
+ public function saveSpentTime($code, $spent) {}
+ public function getAllUsageStat() {}
public function sendUsageStat()
{
// $allStat = $this->getAllUsageStat();
@@ -75,16 +58,8 @@
// // In any case - give user posibility to move futher
// return true;
}
- public function clearUsageStat()
- {
- // $query = 'DELETE FROM @__usage_stat';
- // return dbPps::query($query);
- }
- public function getUserStatsCount()
- {
- // $query = 'SELECT SUM(visits) AS total FROM @__usage_stat';
- // return (int) dbPps::get($query, 'one');
- }
+ public function clearUsageStat() {}
+ public function getUserStatsCount() {}
public function checkAndSend($force = false)
{
// $statCount = $this->getUserStatsCount();
@@ -172,7 +147,6 @@
}
public function bigStatAddCheck($key, $properties = [])
{
- // $canSend = (int) framePps::_()->getModule('options')->get('send_stats');
// if( $canSend ) {
// $this->bigStatAdd( $key, $properties );
// }
@@ -201,7 +175,6 @@
// }
// }
// $this->bigStatAdd('Deactivated', $deactivateParams);
- // $startUsage = (int) framePps::_()->getModule('options')->get('plug_welcome_show');
// if($startUsage) {
// $usedTime = time() - $startUsage;
// $this->bigStatAdd('Used Time', array(
--- a/popup-by-supsystic/pps.php
+++ b/popup-by-supsystic/pps.php
@@ -4,7 +4,7 @@
* Plugin Name: Popup by Supsystic
* Plugin URI: https://supsystic.com/plugins/popup-plugin/
* Description: The Best WordPress popup plugin to help you gain more subscribers, social followers or advertisement. Responsive popups with friendly options
- * Version: 1.12.0
+ * Version: 1.13.0
* Author: supsystic.com
* Author URI: https://supsystic.com
* Text Domain: popup-by-supsystic