Implement xKerman/restricted-unserialize package to guard against unsafe unserialize
authorSeamus Lee <seamuslee001@gmail.com>
Tue, 29 Oct 2019 04:21:06 +0000 (15:21 +1100)
committerSeamus Lee <seamuslee001@gmail.com>
Wed, 20 Nov 2019 21:24:22 +0000 (08:24 +1100)
30 files changed:
CRM/Campaign/BAO/Query.php
CRM/Campaign/Form/Survey/Main.php
CRM/Campaign/Page/AJAX.php
CRM/Contact/BAO/SavedSearch.php
CRM/Contact/Page/SavedSearch.php
CRM/Contribute/Form/ContributionPage/Amount.php
CRM/Core/BAO/Cache.php
CRM/Core/BAO/ConfigSetting.php
CRM/Core/BAO/PrevNextCache.php
CRM/Core/BAO/WordReplacement.php
CRM/Core/Config.php
CRM/Core/DAO.php
CRM/Core/Menu.php
CRM/Extension/ClassLoader.php
CRM/Member/BAO/Membership.php
CRM/Queue/Queue/Memory.php
CRM/Queue/Queue/Sql.php
CRM/Queue/Runner.php
CRM/Report/Form.php
CRM/Utils/Array.php
CRM/Utils/Cache/APCcache.php
CRM/Utils/Cache/ArrayCache.php
CRM/Utils/Cache/ArrayDecorator.php
CRM/Utils/Cache/Memcache.php
CRM/Utils/Cache/Memcached.php
CRM/Utils/Cache/Redis.php
CRM/Utils/Cache/SqlGroup.php
CRM/Utils/String.php
Civi/Core/SettingsBag.php
api/v3/SavedSearch.php

index d44e12800994b1d5ac6a2132cf7780151b299935..e3841d629e5c2dd3afd00f18f7d4f0fb2c8ad8db 100644 (file)
@@ -487,7 +487,7 @@ INNER JOIN  civicrm_custom_group grp on fld.custom_group_id = grp.id
         $recontactInterval = CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey',
           $surveyId, 'recontact_interval'
         );
-        $recontactInterval = unserialize($recontactInterval);
+        $recontactInterval = CRM_Utils_String::unserialize($recontactInterval);
         if ($surveyId &&
           is_array($recontactInterval) &&
           !empty($recontactInterval)
index 68f074ec19d2dec9dddad2653fb4782ccd4a93e8..b1602e3c6ed76a44af46c79c5a6876774b6d7f2b 100644 (file)
@@ -104,7 +104,7 @@ class CRM_Campaign_Form_Survey_Main extends CRM_Campaign_Form_Survey {
       if (!empty($defaults['result_id']) && !empty($defaults['recontact_interval'])) {
 
         $resultId = $defaults['result_id'];
-        $recontactInterval = unserialize($defaults['recontact_interval']);
+        $recontactInterval = CRM_Utils_String::unserialize($defaults['recontact_interval']);
 
         unset($defaults['recontact_interval']);
         $defaults['option_group_id'] = $resultId;
index ecec01993619f5a8578543f4945c00d71b55140b..b3c0df44dfbc4873de51b479daffd52b3e580c37 100644 (file)
@@ -122,7 +122,7 @@ class CRM_Campaign_Page_AJAX {
       $survey->result_id = $id;
       if ($survey->find(TRUE)) {
         if ($survey->recontact_interval) {
-          $recontactInterval = unserialize($survey->recontact_interval);
+          $recontactInterval = CRM_Utils_String::unserialize($survey->recontact_interval);
           foreach ($opValues as $opValId => $opVal) {
             if (is_numeric($recontactInterval[$opVal['label']])) {
               $opValues[$opValId]['interval'] = $recontactInterval[$opVal['label']];
index bea1cf5639179ac103f9f5df83028651898854e8..b936e5022ae4f721c7a5ec8f20a4b78ace962322 100644 (file)
@@ -100,8 +100,8 @@ class CRM_Contact_BAO_SavedSearch extends CRM_Contact_DAO_SavedSearch {
     $fv = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $id, 'form_values');
     $result = NULL;
     if ($fv) {
-      // make sure u unserialize - since it's stored in serialized form
-      $result = unserialize($fv);
+      // make sure u CRM_Utils_String::unserialize - since it's stored in serialized form
+      $result = CRM_Utils_String::unserialize($fv);
     }
 
     $specialFields = ['contact_type', 'group', 'contact_tags', 'member_membership_type_id', 'member_status_id'];
index a101e86283ba456be0876a872b229b670be1b95a..6d35a6205b19bb327bec4b6ba7c094f5258f2681 100644 (file)
@@ -91,7 +91,7 @@ class CRM_Contact_Page_SavedSearch extends CRM_Core_Page {
           $row['description'] = $group->description;
 
           $row['id'] = $savedSearch->id;
-          $formValues = unserialize($savedSearch->form_values);
+          $formValues = CRM_Utils_String::unserialize($savedSearch->form_values);
           $query = new CRM_Contact_BAO_Query($formValues);
           $row['query_detail'] = $query->qill();
 
index ef3d9071c586abdb8b42be1773e1081f681e6ae3..da176fa1eab2def9ac7b43cd3753a9d07701dca8 100644 (file)
@@ -333,7 +333,7 @@ class CRM_Contribute_Form_ContributionPage_Amount extends CRM_Contribute_Form_Co
       }
 
       //CRM-16165, Don't allow reccuring contribution if membership block contain any renewable membership option
-      $membershipTypes = unserialize($membershipBlock->membership_types);
+      $membershipTypes = CRM_Utils_String::unserialize($membershipBlock->membership_types);
       if (!empty($fields['is_recur']) && !empty($membershipTypes)) {
         if (!$membershipBlock->is_separate_payment) {
           $errors['is_recur'] = ts('You need to enable Separate Membership Payment when online contribution page is configured for both Membership and Recurring Contribution.');
index e71e24e368c902d292671ab01d411a4cb224f4dd..4ef618f30d1395f9ad4a5f8dfbf0ae24d2880f17 100644 (file)
@@ -444,10 +444,10 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache {
     // Upgrade support -- old records (serialize) always have this punctuation,
     // and new records (base64) never do.
     if (strpos($string, ':') !== FALSE || strpos($string, ';') !== FALSE) {
-      return unserialize($string);
+      return CRM_Utils_String::unserialize($string);
     }
     else {
-      return unserialize(base64_decode($string));
+      return CRM_Utils_String::unserialize(base64_decode($string));
     }
   }
 
index 8919a6d79095ec8cbb583e4a82875029bb12e897..c30f7ae1ce89ec1607442d74a9ebbe647261bed5 100644 (file)
@@ -63,7 +63,7 @@ class CRM_Core_BAO_ConfigSetting {
     $domain->id = CRM_Core_Config::domainID();
     $domain->find(TRUE);
     if ($domain->config_backend) {
-      $params = array_merge(unserialize($domain->config_backend), $params);
+      $params = array_merge(CRM_Utils_String::unserialize($domain->config_backend), $params);
     }
 
     $params = CRM_Core_BAO_ConfigSetting::filterSkipVars($params);
@@ -106,7 +106,7 @@ class CRM_Core_BAO_ConfigSetting {
     $domain->id = CRM_Core_Config::domainID();
     $domain->find(TRUE);
     if ($domain->config_backend) {
-      $defaults = unserialize($domain->config_backend);
+      $defaults = CRM_Utils_String::unserialize($domain->config_backend);
       if ($defaults === FALSE || !is_array($defaults)) {
         $defaults = [];
         return FALSE;
index f217d874417f1d22551cff250d0e747f5fc039d2..7d7cc05a7b0055265ef85c853f5dc35189c631ee 100644 (file)
@@ -302,7 +302,7 @@ FROM   civicrm_prevnext_cache pn
     $count = 0;
     while ($dao->fetch()) {
       if (self::is_serialized($dao->data)) {
-        $main[$count] = unserialize($dao->data);
+        $main[$count] = CRM_Utils_String::unserialize($dao->data);
       }
       else {
         $main[$count] = $dao->data;
@@ -334,7 +334,7 @@ FROM   civicrm_prevnext_cache pn
    * @return bool
    */
   public static function is_serialized($string) {
-    return (@unserialize($string) !== FALSE);
+    return (@CRM_Utils_String::unserialize($string) !== FALSE);
   }
 
   /**
@@ -507,7 +507,7 @@ WHERE (pn.cachekey $op %1 OR pn.cachekey $op %2)
     foreach ($prevNextId as $id) {
       $dao->id = $id;
       if ($dao->find(TRUE)) {
-        $originalData = unserialize($dao->data);
+        $originalData = CRM_Utils_String::unserialize($dao->data);
         $srcFields = ['ID', 'Name'];
         $swapFields = ['srcID', 'srcName', 'dstID', 'dstName'];
         $data = array_diff_assoc($originalData, array_fill_keys($swapFields, 1));
index f77891ab2564e59026b9480e0633ab39a86a7747..16099a1c27a0321367abb54b047a25f772ff8ab7 100644 (file)
@@ -239,7 +239,7 @@ WHERE  domain_id = %1
         $params["domain_id"] = $value["id"];
         $params["options"] = ['wp-rebuild' => $rebuildEach];
         // Unserialize word match string.
-        $localeCustomArray = unserialize($value["locale_custom_strings"]);
+        $localeCustomArray = CRM_Utils_String::unserialize($value["locale_custom_strings"]);
         if (!empty($localeCustomArray)) {
           $wordMatchArray = [];
           // Only return the replacement strings of the current language,
@@ -315,7 +315,7 @@ WHERE  domain_id = %1
       1 => [$domainId, 'Integer'],
     ]);
     while ($domain->fetch()) {
-      return empty($domain->locale_custom_strings) ? [] : unserialize($domain->locale_custom_strings);
+      return empty($domain->locale_custom_strings) ? [] : CRM_Utils_String::unserialize($domain->locale_custom_strings);
     }
   }
 
index f8952cf41928c30621be8a62829c05314df4f383..d63d3d9b42511d57509adf95695e5c7d00ca480a 100644 (file)
@@ -565,7 +565,7 @@ class CRM_Core_Config extends CRM_Core_Config_MagicMerge {
       WHERE is_domain = 1 AND name = "installed"
     ');
     while ($dao->fetch()) {
-      $value = unserialize($dao->value);
+      $value = CRM_Utils_String::unserialize($dao->value);
       if (!empty($value)) {
         Civi::settings()->set('installed', 1);
         return;
index b508b64e6c278a76225998eedfe1e70443aadfd8..c2f024face8a673a2a286b99a5f50a6dc843e786 100644 (file)
@@ -2939,7 +2939,7 @@ SELECT contact_id
         return strlen($value) ? json_decode($value, TRUE) : [];
 
       case self::SERIALIZE_PHP:
-        return strlen($value) ? unserialize($value, ['allowed_classes' => FALSE]) : [];
+        return strlen($value) ? CRM_Utils_String::unserialize($value) : [];
 
       case self::SERIALIZE_COMMA:
         return explode(',', trim(str_replace(', ', '', $value)));
index c6fefdc91a5951343e3edf09bacfff9781cbea00..6f19e4a573e3282376ecf8b6b91ba258b794d58f 100644 (file)
@@ -609,13 +609,13 @@ UNION (
       // Move module_data into main item.
       if (isset(self::$_menuCache[$menu->path]['module_data'])) {
         CRM_Utils_Array::extend(self::$_menuCache[$menu->path],
-          unserialize(self::$_menuCache[$menu->path]['module_data']));
+          CRM_Utils_String::unserialize(self::$_menuCache[$menu->path]['module_data']));
         unset(self::$_menuCache[$menu->path]['module_data']);
       }
 
       // Unserialize other elements.
       foreach (self::$_serializedElements as $element) {
-        self::$_menuCache[$menu->path][$element] = unserialize($menu->$element);
+        self::$_menuCache[$menu->path][$element] = CRM_Utils_String::unserialize($menu->$element);
 
         if (strpos($path, $menu->path) !== FALSE) {
           $menuPath = &self::$_menuCache[$menu->path];
index 4569f6c2e4ee498b2af85609e04db30b22d59fe7..d0845911e023305ef5f24e2413ab209cbbec53dc 100644 (file)
@@ -89,7 +89,7 @@ class CRM_Extension_ClassLoader {
       $loader = $this->buildClassLoader();
       $ser = serialize($loader);
       file_put_contents($file,
-        sprintf("<?php\nreturn unserialize(%s);", var_export($ser, 1))
+        sprintf("<?php\nreturn CRM_Utils_String::unserialize(%s);", var_export($ser, 1))
       );
     }
     return $loader->register();
index 9ab78c4ad2c9a4d8bdf71401f9f6f565296c4f29..feaa35fef172c4cb6cdb8562c6a0e7b3a6e11eab 100644 (file)
@@ -754,7 +754,7 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
     if ($dao->find(TRUE)) {
       CRM_Core_DAO::storeValues($dao, $membershipBlock);
       if (!empty($membershipBlock['membership_types'])) {
-        $membershipTypes = unserialize($membershipBlock['membership_types']);
+        $membershipTypes = CRM_Utils_String::unserialize($membershipBlock['membership_types']);
         if (!is_array($membershipTypes)) {
           return $membershipBlock;
         }
index 0c840c1c366d1e4bd5c0f9b55863f87f84ea329e..99b92959eebfc9681c55125c3d860ee73ff70b09 100644 (file)
@@ -137,7 +137,7 @@ class CRM_Queue_Queue_Memory extends CRM_Queue_Queue {
 
         $item = new stdClass();
         $item->id = $id;
-        $item->data = unserialize($data);
+        $item->data = CRM_Utils_String::unserialize($data);
         return $item;
       }
       else {
@@ -166,7 +166,7 @@ class CRM_Queue_Queue_Memory extends CRM_Queue_Queue {
 
       $item = new stdClass();
       $item->id = $id;
-      $item->data = unserialize($data);
+      $item->data = CRM_Utils_String::unserialize($data);
       return $item;
     }
     // nothing in queue
index 28f13506de65ccdb4fcf702506fc635eda026b84..dc9474c1a13bd74475be75568570f087fea93036 100644 (file)
@@ -160,7 +160,7 @@ class CRM_Queue_Queue_Sql extends CRM_Queue_Queue {
       #        $dao->submit_time = date('YmdHis', strtotime($dao->submit_time));
       #        $dao->release_time = date('YmdHis', $nowEpoch + $lease_time);
       #        $dao->save();
-      $dao->data = unserialize($dao->data);
+      $dao->data = CRM_Utils_String::unserialize($dao->data);
       $result = $dao;
     }
 
@@ -196,7 +196,7 @@ class CRM_Queue_Queue_Sql extends CRM_Queue_Queue {
         '1' => [date('YmdHis', $nowEpoch + $lease_time), 'String'],
         '2' => [$dao->id, 'Integer'],
       ]);
-      $dao->data = unserialize($dao->data);
+      $dao->data = CRM_Utils_String::unserialize($dao->data);
       return $dao;
     }
   }
index fa2817e7130acb761c61d7794084899874fdb40b..48906b6abbda1717488240008167b57a93fa3e8c 100644 (file)
@@ -85,7 +85,7 @@ class CRM_Queue_Runner {
    */
   public static function instance($qrid) {
     if (!empty($_SESSION['queueRunners'][$qrid])) {
-      return unserialize($_SESSION['queueRunners'][$qrid]);
+      return CRM_Utils_String::unserialize($_SESSION['queueRunners'][$qrid]);
     }
     else {
       return NULL;
index 783026daa2737ae87d450d72d459a8ab3b56bc53..918e24c997d01de08e050bfa7660a48f49f215a6 100644 (file)
@@ -632,7 +632,7 @@ class CRM_Report_Form extends CRM_Core_Form {
 
       $formValues = CRM_Utils_Array::value('form_values', $this->_instanceValues);
       if ($formValues) {
-        $this->_formValues = unserialize($formValues);
+        $this->_formValues = CRM_Utils_String::unserialize($formValues);
       }
       else {
         $this->_formValues = NULL;
index 7dd32cd4be1d43e4a493ddd044666ebf220c8bda..b28f6777034d626df0208a837a4e1516fad876c5 100644 (file)
@@ -499,7 +499,7 @@ class CRM_Utils_Array {
    *   The input array with duplicate values removed.
    */
   public static function crmArrayUnique($array) {
-    $result = array_map("unserialize", array_unique(array_map("serialize", $array)));
+    $result = array_map("CRM_Utils_String::unserialize", array_unique(array_map("serialize", $array)));
     foreach ($result as $key => $value) {
       if (is_array($value)) {
         $result[$key] = self::crmArrayUnique($value);
index 187da7edb801bee1a059c5aa1c7838718186c8e5..80c3ac6a2b597919277da13eee7384bcd8b5f275 100644 (file)
@@ -145,7 +145,7 @@ class CRM_Utils_Cache_APCcache implements CRM_Utils_Cache_Interface {
   }
 
   private function reobjectify($value) {
-    return is_object($value) ? unserialize(serialize($value)) : $value;
+    return is_object($value) ? CRM_Utils_String::unserialize(serialize($value)) : $value;
   }
 
 }
index c83f3f1f4eb63448bfaef67f2226ffd78e14f2e7..de44d73b647c121c6f93bed187cd1afbab8fd905 100644 (file)
@@ -121,12 +121,12 @@ class CRM_Utils_Cache_ArrayCache implements CRM_Utils_Cache_Interface {
 
   private function reobjectify($value) {
     if (is_object($value)) {
-      return unserialize(serialize($value));
+      return CRM_Utils_String::unserialize(serialize($value));
     }
     if (is_array($value)) {
       foreach ($value as $p) {
         if (is_object($p)) {
-          return unserialize(serialize($value));
+          return CRM_Utils_String::unserialize(serialize($value));
         }
       }
     }
index 86ac79728a52e65ffbe3ed41308d3c14ecc27033..0f75d84a1a2b76c005e66f342fc1fcce3efb4886 100644 (file)
@@ -137,7 +137,7 @@ class CRM_Utils_Cache_ArrayDecorator implements CRM_Utils_Cache_Interface {
   }
 
   private function reobjectify($value) {
-    return is_object($value) ? unserialize(serialize($value)) : $value;
+    return is_object($value) ? CRM_Utils_String::unserialize(serialize($value)) : $value;
   }
 
 }
index 511663792e575030897694f495f184a2f505ce73..64db5ae9f1dff935c046a551582532458a6cf651 100644 (file)
@@ -149,7 +149,7 @@ class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
   public function get($key, $default = NULL) {
     CRM_Utils_Cache::assertValidKey($key);
     $result = $this->_cache->get($this->getTruePrefix() . $key);
-    return ($result === FALSE) ? $default : unserialize($result);
+    return ($result === FALSE) ? $default : CRM_Utils_String::unserialize($result);
   }
 
   /**
index fd3aee39c236fe795e16141fab26cc9d5b05361c..6e2a1332a528cd48424d9681163f44653bb0330c 100644 (file)
@@ -167,7 +167,7 @@ class CRM_Utils_Cache_Memcached implements CRM_Utils_Cache_Interface {
     $result = $this->_cache->get($key);
     switch ($this->_cache->getResultCode()) {
       case Memcached::RES_SUCCESS:
-        return unserialize($result);
+        return CRM_Utils_String::unserialize($result);
 
       case Memcached::RES_NOTFOUND:
         return $default;
index f7c8435f791eac16f84e69e15d2fc304ec12446e..b3b01fee25488e122d4fde3b9111828d629ed538 100644 (file)
@@ -153,7 +153,7 @@ class CRM_Utils_Cache_Redis implements CRM_Utils_Cache_Interface {
   public function get($key, $default = NULL) {
     CRM_Utils_Cache::assertValidKey($key);
     $result = $this->_cache->get($this->_prefix . $key);
-    return ($result === FALSE) ? $default : unserialize($result);
+    return ($result === FALSE) ? $default : CRM_Utils_String::unserialize($result);
   }
 
   /**
index 840886c65edca956eb8c5bf66cdcb2cdfda1a535..b206c1b04cc736af7b2504b7a9c3b9189f65cc31 100644 (file)
@@ -184,7 +184,7 @@ class CRM_Utils_Cache_SqlGroup implements CRM_Utils_Cache_Interface {
   }
 
   private function reobjectify($value) {
-    return is_object($value) ? unserialize(serialize($value)) : $value;
+    return is_object($value) ? CRM_Utils_String::unserialize(serialize($value)) : $value;
   }
 
   /**
index d0f295dd18048cd8121fd3bc9b959124572d2ae0..44c415336870d0465759d6a07f1b95b4d4fb6b2c 100644 (file)
@@ -31,6 +31,9 @@
  * @copyright CiviCRM LLC (c) 2004-2019
  */
 
+use function xKerman\Restricted\unserialize;
+use xKerman\Restricted\UnserializeFailedException;
+
 require_once 'HTML/QuickForm/Rule/Email.php';
 
 /**
@@ -936,4 +939,23 @@ class CRM_Utils_String {
     return array_values(array_unique($result));
   }
 
+  /**
+   * Use xkerman/restricted-unserialize to unserialize a string of data.
+   * @param string|NULL $string
+   *
+   * @return mixed
+   * @throws CRM_Core_Exception
+   */
+  public static function unserialize($string) {
+    if (!is_string($string)) {
+      return FALSE;
+    }
+    try {
+      return unserialize($string);
+    }
+    catch (UnserializeFailedException $e) {
+      throw new CRM_Core_Exception($e->getMessage());
+    }
+  } 
+
 }
index 4155e7baf86d4e1fe2069a63c938d55011538eec..bd419c0fca7a9d4296a3b47d6f102184eb00b446 100644 (file)
@@ -150,7 +150,7 @@ class SettingsBag {
     if (!$isUpgradeMode || \CRM_Core_DAO::checkTableExists('civicrm_setting')) {
       $dao = \CRM_Core_DAO::executeQuery($this->createQuery()->toSQL());
       while ($dao->fetch()) {
-        $this->values[$dao->name] = ($dao->value !== NULL) ? unserialize($dao->value) : NULL;
+        $this->values[$dao->name] = ($dao->value !== NULL) ? \CRM_Utils_String::unserialize($dao->value) : NULL;
       }
     }
 
@@ -355,7 +355,7 @@ class SettingsBag {
       foreach ($metadata['on_change'] as $callback) {
         call_user_func(
           \Civi\Core\Resolver::singleton()->get($callback),
-          unserialize($dao->value),
+          \CRM_Utils_String::unserialize($dao->value),
           $value,
           $metadata,
           $this->domainId
index c515e890e247d45cd302bf9172b2f0b6dbd9cff2..a379b7f4a4a2703adc999b13d15cac1bf5692911 100644 (file)
@@ -57,7 +57,7 @@ function civicrm_api3_saved_search_create($params) {
     }
     else {
       // Assume that form_values is serialized.
-      $params["formValues"] = unserialize($params["form_values"]);
+      $params["formValues"] = CRM_Utils_String::unserialize($params["form_values"]);
     }
   }
 
@@ -109,7 +109,7 @@ function _civicrm_api3_saved_search_result_cleanup(&$result) {
     // Only clean up the values if there are values. (A getCount operation
     // for example does not return values.)
     foreach ($result['values'] as $key => $value) {
-      $result['values'][$key]['form_values'] = unserialize($value['form_values']);
+      $result['values'][$key]['form_values'] = CRM_Utils_String::unserialize($value['form_values']);
     }
   }
 }