From 6fa1da686a9f42831e1652dd0bf57776a2222bfc Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:26:08 +0800 Subject: [PATCH 01/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-19=EF=BC=9ASto?= =?UTF-8?q?red=20XSS=20in=20Grant=20Type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/CRM/Event/Form/Selector.tpl | 2 +- templates/CRM/Grant/Form/GrantView.tpl | 4 ++-- templates/CRM/Grant/Form/Task/Print.tpl | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/CRM/Event/Form/Selector.tpl b/templates/CRM/Event/Form/Selector.tpl index 023ce0bca2..0484f52b6f 100644 --- a/templates/CRM/Event/Form/Selector.tpl +++ b/templates/CRM/Event/Form/Selector.tpl @@ -110,7 +110,7 @@ {$row.participant_register_date|crmDate} {$row.participant_status} - {$row.participant_role_id} + {$row.participant_role_id|escape} {$row.action|replace:'xx':$participant_id} {/foreach} diff --git a/templates/CRM/Grant/Form/GrantView.tpl b/templates/CRM/Grant/Form/GrantView.tpl index a7ab9ebc05..281e0e45b8 100644 --- a/templates/CRM/Grant/Form/GrantView.tpl +++ b/templates/CRM/Grant/Form/GrantView.tpl @@ -45,8 +45,8 @@ - - + + diff --git a/templates/CRM/Grant/Form/Task/Print.tpl b/templates/CRM/Grant/Form/Task/Print.tpl index b43a44c5fc..5d4c1efb3c 100644 --- a/templates/CRM/Grant/Form/Task/Print.tpl +++ b/templates/CRM/Grant/Form/Task/Print.tpl @@ -44,8 +44,8 @@ {foreach from=$rows item=row} - - + + From 9fef1e449838208cbf8f2d80ddbefe55e5c943d9 Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:26:38 +0800 Subject: [PATCH 02/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-20=EF=BC=9ASto?= =?UTF-8?q?red=20XSS=20in=20Website=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/CRM/Contact/Page/View/Summary.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/CRM/Contact/Page/View/Summary.tpl b/templates/CRM/Contact/Page/View/Summary.tpl index 92456b9cf9..1c1848e815 100644 --- a/templates/CRM/Contact/Page/View/Summary.tpl +++ b/templates/CRM/Contact/Page/View/Summary.tpl @@ -204,8 +204,8 @@ {foreach from=$website item=item} {if $item.url} - - + + {/if} From 6ab001e8e8d3558885e0f8684faaa43f6ed1ac67 Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:27:37 +0800 Subject: [PATCH 03/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-21,SA-2026-27?= =?UTF-8?q?=EF=BC=9AEvent=20Template=20Title=20/=20Participant=20Status=20?= =?UTF-8?q?XSS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/CRM/Admin/Page/EventTemplate.tpl | 6 +++--- templates/CRM/Event/Page/ManageEvent.tpl | 2 +- templates/CRM/Grant/Form/Selector.tpl | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/CRM/Admin/Page/EventTemplate.tpl b/templates/CRM/Admin/Page/EventTemplate.tpl index c6979bb922..2ca4da3ef6 100644 --- a/templates/CRM/Admin/Page/EventTemplate.tpl +++ b/templates/CRM/Admin/Page/EventTemplate.tpl @@ -55,9 +55,9 @@ {foreach from=$rows item=row} - - - + + + diff --git a/templates/CRM/Event/Page/ManageEvent.tpl b/templates/CRM/Event/Page/ManageEvent.tpl index 41609e3288..cd00685ad7 100644 --- a/templates/CRM/Event/Page/ManageEvent.tpl +++ b/templates/CRM/Event/Page/ManageEvent.tpl @@ -61,7 +61,7 @@ {foreach from=$rows item=row} - + {/if} - - + + From 93f81984301a2037853dee6ac5b6b019297c91b8 Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:29:08 +0800 Subject: [PATCH 04/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-22=EF=BC=9ASto?= =?UTF-8?q?red=20XSS=20in=20Membership=20Type,SA-2026-23=EF=BC=9AStored=20?= =?UTF-8?q?XSS=20in=20Price=20Field=20label?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/CRM/Member/Form/MembershipView.tpl | 2 +- templates/CRM/Member/Form/Selector.tpl | 4 ++-- templates/CRM/Member/Page/MembershipStatus.tpl | 2 +- templates/CRM/Price/Page/Field.tpl | 2 +- templates/CRM/Price/Page/table.tpl | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/templates/CRM/Member/Form/MembershipView.tpl b/templates/CRM/Member/Form/MembershipView.tpl index b37fca663b..b32784aa25 100644 --- a/templates/CRM/Member/Form/MembershipView.tpl +++ b/templates/CRM/Member/Form/MembershipView.tpl @@ -54,7 +54,7 @@ {if $owner_display_name} {/if} - + diff --git a/templates/CRM/Member/Form/Selector.tpl b/templates/CRM/Member/Form/Selector.tpl index 19424c16a2..c5dee3750d 100644 --- a/templates/CRM/Member/Form/Selector.tpl +++ b/templates/CRM/Member/Form/Selector.tpl @@ -61,8 +61,8 @@ {$row.sort_name} {/if} - diff --git a/templates/CRM/Member/Page/MembershipStatus.tpl b/templates/CRM/Member/Page/MembershipStatus.tpl index 416248d873..43a93e6061 100644 --- a/templates/CRM/Member/Page/MembershipStatus.tpl +++ b/templates/CRM/Member/Page/MembershipStatus.tpl @@ -55,7 +55,7 @@ {foreach from=$rows item=row} - + diff --git a/templates/CRM/Price/Page/Field.tpl b/templates/CRM/Price/Page/Field.tpl index e986a3876e..b18d5cfbc7 100644 --- a/templates/CRM/Price/Page/Field.tpl +++ b/templates/CRM/Price/Page/Field.tpl @@ -76,7 +76,7 @@ {foreach from=$priceField key=fid item=row} - + diff --git a/templates/CRM/Price/Page/table.tpl b/templates/CRM/Price/Page/table.tpl index b1404258c4..c55fc894d9 100644 --- a/templates/CRM/Price/Page/table.tpl +++ b/templates/CRM/Price/Page/table.tpl @@ -37,8 +37,8 @@ {foreach from=$usedBy.civicrm_event item=event key=id} - - + + @@ -57,8 +57,8 @@ {foreach from=$usedBy.civicrm_contribution_page item=contributionPage key=id} - - + + {/foreach} From 95a14df12224d5f5c080e86c2bd759d58b7685ca Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:30:32 +0800 Subject: [PATCH 05/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-25=EF=BC=9ASto?= =?UTF-8?q?red=20XSS=20in=20Tag=20Name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/CRM/Tag/Form/Tag.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/CRM/Tag/Form/Tag.tpl b/templates/CRM/Tag/Form/Tag.tpl index a21923a4e8..ac88a81044 100644 --- a/templates/CRM/Tag/Form/Tag.tpl +++ b/templates/CRM/Tag/Form/Tag.tpl @@ -125,19 +125,19 @@ function initTagTree() {
  • {if ! $node.children}{/if} {if $node.children}{/if} - {if $node.children} {/if} + {if $node.children} {/if} {if $node.children}
      {foreach from=$node.children item="subnode" key="subid"}
    • - {if $subnode.children} {/if} + {if $subnode.children} {/if} {if $subnode.children}
        {foreach from=$subnode.children item="subsubnode" key="subsubid"}
      • - +
      • {/foreach}
      From a697da80f749fa6efa9e4a5843cbb7c526f853d5 Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:31:30 +0800 Subject: [PATCH 06/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-26=EF=BC=9AUna?= =?UTF-8?q?uthorized=20access=20to=20Files=20via=20APIv3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CRM/Core/Page/File.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CRM/Core/Page/File.php b/CRM/Core/Page/File.php index d0e429b5f4..d0cb785fce 100644 --- a/CRM/Core/Page/File.php +++ b/CRM/Core/Page/File.php @@ -67,6 +67,12 @@ public function run() { CRM_Core_Error::fatal('Could not retrieve the file'); } + if (empty($downloadName) && substr($path, -8) === '.unknown') { + $mimeType = 'application/octet-stream'; + header('Content-Security-Policy: default-src \'none\''); + header('X-Content-Type-Options: nosniff'); + } + $buffer = file_get_contents($path); if ($buffer === FALSE) { CRM_Core_Error::fatal('The file is either empty or you do not have permission to retrieve the file'); From c8f11116831b1a4fdfb0458bf505c960011c80cc Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:31:58 +0800 Subject: [PATCH 07/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-28=EF=BC=9AEsc?= =?UTF-8?q?alation=20via=20Extension=20Download=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v3/Extension.php | 283 ------------------------------------------- 1 file changed, 283 deletions(-) delete mode 100644 api/v3/Extension.php diff --git a/api/v3/Extension.php b/api/v3/Extension.php deleted file mode 100644 index 59f2afd46c..0000000000 --- a/api/v3/Extension.php +++ /dev/null @@ -1,283 +0,0 @@ -getManager()->install($keys); - } - catch (CRM_Extension_Exception $e) { - return civicrm_api3_create_error($e->getMessage()); - } - - return civicrm_api3_create_success(); -} - -/** - * Enable an extension - * - * @param array $params input parameters - * - key: string, eg "com.example.myextension" - * - keys: mixed; array of string, eg array("com.example.myextension1", "com.example.myextension2") or string with comma-delimited list - * using 'keys' should be more performant than making multiple API calls with 'key' - * - * @return array API result - * @static void - * @access public - * @example ExtensionEnable.php - * - */ -function civicrm_api3_extension_enable($params) { - $keys = _civicrm_api3_getKeys($params); - if (count($keys) == 0) { - return civicrm_api3_create_success(); - } - - CRM_Extension_System::singleton()->getManager()->enable($keys); - return civicrm_api3_create_success(); -} - -/** - * Disable an extension - * - * @param array $params input parameters - * - key: string, eg "com.example.myextension" - * - keys: mixed; array of string, eg array("com.example.myextension1", "com.example.myextension2") or string with comma-delimited list - * using 'keys' should be more performant than making multiple API calls with 'key' - * - * @return array API result - * @static void - * @access public - * @example ExtensionDisable.php - * - */ -function civicrm_api3_extension_disable($params) { - $keys = _civicrm_api3_getKeys($params); - if (count($keys) == 0) { - return civicrm_api3_create_success(); - } - - CRM_Extension_System::singleton()->getManager()->disable($keys); - return civicrm_api3_create_success(); -} - -/** - * Uninstall an extension - * - * @param array $params input parameters - * - key: string, eg "com.example.myextension" - * - keys: array of string, eg array("com.example.myextension1", "com.example.myextension2") - * using 'keys' should be more performant than making multiple API calls with 'key' - * - removeFiles: bool, whether to remove source tree; default: FALSE - * - * @return array API result - * @static void - * @access public - * @example ExtensionUninstall.php - * - */ -function civicrm_api3_extension_uninstall($params) { - $keys = _civicrm_api3_getKeys($params); - if (count($keys) == 0) { - return civicrm_api3_create_success(); - } - - // TODO // $removeFiles = CRM_Utils_Array::value('removeFiles', $params, FALSE); - CRM_Extension_System::singleton()->getManager()->uninstall($keys); - return civicrm_api3_create_success(); -} - -/** - * Download and install an extension - * - * @param array $params input parameters - * - key: string, eg "com.example.myextension" - * - url: string eg "http://repo.com/myextension-1.0.zip" - * - * @return array API result - * @static void - * @access public - * @example ExtensionDownload.php - * - */ -function civicrm_api3_extension_download($params) { - if (!CRM_Utils_Array::arrayKeyExists('key', $params)) { - throw new API_Exception('Missing required parameter: key'); - } - - if (!CRM_Utils_Array::arrayKeyExists('url', $params)) { - if (!CRM_Extension_System::singleton()->getBrowser()->isEnabled()) { - throw new API_Exception('Automatic downloading is diabled. Try adding parameter "url"'); - } - if ($reqs = CRM_Extension_System::singleton()->getBrowser()->checkRequirements()) { - $first = array_shift($reqs); - throw new API_Exception($first['message']); - } - if ($info = CRM_Extension_System::singleton()->getBrowser()->getExtension($params['key'])) { - if ($info->downloadUrl) { - $params['url'] = $info->downloadUrl; - } - } - } - - if (!CRM_Utils_Array::arrayKeyExists('url', $params)) { - throw new API_Exception('Cannot resolve download url for extension. Try adding parameter "url"'); - } - - foreach (CRM_Extension_System::singleton()->getDownloader()->checkRequirements() as $requirement) { - return civicrm_api3_create_error($requirement['message']); - } - - if (!CRM_Extension_System::singleton()->getDownloader()->download($params['key'], $params['url'])) { - return civicrm_api3_create_error('Download failed - ZIP file is unavailable or malformed'); - } - CRM_Extension_System::singleton()->getCache()->flush(); - CRM_Extension_System::singleton(TRUE); - CRM_Extension_System::singleton()->getManager()->install([$params['key']]); - - return civicrm_api3_create_success(); -} - -/** - * Download and install an extension - * - * @param array $params input parameters - * - local: bool, whether to rescan local filesystem (default: TRUE) - * - remote: bool, whether to rescan remote repository (default: TRUE) - * - * @return array API result - * @static void - * @access public - * @example ExtensionRefresh.php - * - */ -function civicrm_api3_extension_refresh($params) { - $defaults = ['local' => TRUE, 'remote' => TRUE]; - $params = array_merge($defaults, $params); - - $system = CRM_Extension_System::singleton(TRUE); - - if ($params['local']) { - $system->getManager()->refresh(); - $system->getManager()->getStatuses(); // force immediate scan - } - - if ($params['remote']) { - if ($system->getBrowser()->isEnabled() && empty($system->getBrowser()->checkRequirements)) { - $system->getBrowser()->refresh(); - $system->getBrowser()->getExtensions(); // force immediate download - } - } - - return civicrm_api3_create_success(); -} - -/** - * Get a list of available extensions - * - * @return array API result - * @static void - * @access public - * @example ExtensionGet.php - * - */ -function civicrm_api3_extension_get($params) { - $statuses = CRM_Extension_System::singleton()->getManager()->getStatuses(); - $mapper = CRM_Extension_System::singleton()->getMapper(); - $result = []; - foreach ($statuses as $key => $status) { - //try { - // $info = (array) $mapper->keyToInfo($key); - //} catch (CRM_Extension_Exception $e) { - $info = []; - $info['key'] = $key; - //} - $info['status'] = $status; - $result[] = $info; - } - return civicrm_api3_create_success($result); -} - -/** - * Determine the list of extension keys - * - * @param array $params API request params with 'key' or 'keys' - * @return array of extension keys - * @throws API_Exception - */ -function _civicrm_api3_getKeys($params) { - if (CRM_Utils_Array::arrayKeyExists('keys', $params) && is_array($params['keys'])) { - return $params['keys']; - } - elseif (CRM_Utils_Array::arrayKeyExists('keys', $params) && is_string($params['keys'])) { - if ($params['keys'] == '') { - return []; - } - else { - return explode(API_V3_EXTENSION_DELIMITER, $params['keys']); - } - } - elseif (CRM_Utils_Array::arrayKeyExists('key', $params)) { - return [$params['key']]; - } - else { - throw new API_Exception('Missing required parameter: key or keys'); - } -} From cddc1bffc070a068cc63982dcb60bbb92e6b0ffd Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:33:34 +0800 Subject: [PATCH 08/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-30=EF=BC=9ASto?= =?UTF-8?q?red=20XSS=20in=20File=20Attachments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/CRM/Contact/Page/View/CustomDataFieldView.tpl | 2 +- templates/CRM/Custom/Page/CustomDataView.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl b/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl index 7f75828d86..ee3ea45571 100644 --- a/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl +++ b/templates/CRM/Contact/Page/View/CustomDataFieldView.tpl @@ -38,7 +38,7 @@ {if $element.field_value.image}
  • {else} - + {/if} {elseif $element.field_data_type EQ 'ContactReference' && $element.contact_ref_id} {*Contact ref id passed if user has sufficient permissions - so make a link.*} diff --git a/templates/CRM/Custom/Page/CustomDataView.tpl b/templates/CRM/Custom/Page/CustomDataView.tpl index 294384290e..f9a9e41609 100644 --- a/templates/CRM/Custom/Page/CustomDataView.tpl +++ b/templates/CRM/Custom/Page/CustomDataView.tpl @@ -72,7 +72,7 @@ {if $element.field_value.image} {else} - + {/if} {else} From c517b973839093d6e5dde9d058c65c92f0ff1a9b Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:34:12 +0800 Subject: [PATCH 09/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-31=EF=BC=9ASQL?= =?UTF-8?q?I=20in=20GroupContact=20Create=20APIv3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CRM/Contact/BAO/GroupContact.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CRM/Contact/BAO/GroupContact.php b/CRM/Contact/BAO/GroupContact.php index 339af539ef..02dbe2c61b 100644 --- a/CRM/Contact/BAO/GroupContact.php +++ b/CRM/Contact/BAO/GroupContact.php @@ -694,6 +694,11 @@ public static function bulkAddContactsToGroup( $tracking = NULL ) { + $contactIDs = array_map('intval', $contactIDs); + $groupID = (int) $groupID; + $method = CRM_Core_DAO::escapeString($method); + $status = CRM_Core_DAO::escapeString($status); + $tracking = CRM_Core_DAO::escapeString($tracking ?: ''); $numContactsAdded = 0; $numContactsNotAdded = 0; From df58c19ee47550edcfc734a4f8297a90e61f68bd Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:34:46 +0800 Subject: [PATCH 10/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-32=EF=BC=9ASto?= =?UTF-8?q?red=20XSS=20in=20Profile=20Help?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/CRM/Profile/Form/Dynamic.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/CRM/Profile/Form/Dynamic.tpl b/templates/CRM/Profile/Form/Dynamic.tpl index fb020a21a1..997e822ddf 100644 --- a/templates/CRM/Profile/Form/Dynamic.tpl +++ b/templates/CRM/Profile/Form/Dynamic.tpl @@ -55,7 +55,7 @@ {if $field.groupTitle != $fieldset} {if $fieldset != $zeroField} {if $groupHelpPost} -
    {$groupHelpPost}
    +
    {$groupHelpPost|purify}
    {/if} {if $mode neq 8 && $mode neq 4} @@ -71,7 +71,7 @@ {assign var=fieldset value=`$field.groupTitle`} {assign var=groupHelpPost value=`$field.groupHelpPost`} {if $field.groupHelpPre} -
    {$field.groupHelpPre}
    +
    {$field.groupHelpPre|purify}
    {/if}
    {/if} @@ -158,7 +158,7 @@ {* Show explanatory text for field if not in 'view' mode *} {if $field.help_post && $action neq 4 && $form.$n.html}
    -
    {$field.help_post}
    +
    {$field.help_post|purify}
    {/if} {/if}{* end of main if field name if *} @@ -172,7 +172,7 @@ {/if} {if $field.groupHelpPost} -
    {$field.groupHelpPost}
    +
    {$field.groupHelpPost|purify}
    {/if} {if $mode neq 8 && $mode neq 4} From 1a0035ddf2511f7b61e60d12e1983a545de1fffa Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:36:09 +0800 Subject: [PATCH 11/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-24=EF=BC=9ARCE?= =?UTF-8?q?=20via=20File=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v3/File.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/v3/File.php b/api/v3/File.php index 613dbdcb38..1086e4fa43 100644 --- a/api/v3/File.php +++ b/api/v3/File.php @@ -61,6 +61,13 @@ function civicrm_api3_file_create($params) { civicrm_api3_verify_mandatory($params, 'CRM_Core_DAO_File', ['uri']); + // Security: Validate URI to prevent path traversal attacks + if (isset($params['uri']) && $params['uri'] !== basename($params['uri'])) { + throw new CRM_Core_Exception( + 'Invalid URI: must not contain directory separators or path traversal sequences' + ); + } + if (!isset($params['upload_date'])) { $params['upload_date'] = date("Ymd"); } From ca7adb3cd2db0830c3466c415aa9a4f19bcfebfb Mon Sep 17 00:00:00 2001 From: liyin Date: Thu, 6 Aug 2026 11:36:58 +0800 Subject: [PATCH 12/12] =?UTF-8?q?Refs=20#47100,feat:SA-2026-24=EF=BC=9ARCE?= =?UTF-8?q?=20via=20File=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CRM/Core/BAO/File.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CRM/Core/BAO/File.php b/CRM/Core/BAO/File.php index fa55f41feb..074fe3f225 100644 --- a/CRM/Core/BAO/File.php +++ b/CRM/Core/BAO/File.php @@ -161,6 +161,14 @@ public static function filePostProcess( $config = &CRM_Core_Config::singleton(); + // Security: validate file path is within CiviCRM upload directory (security/core#173) + $realData = realpath($data); + $uploadDir = realpath($config->uploadDir); + if (!$realData || !$uploadDir || strpos($realData, $uploadDir . DIRECTORY_SEPARATOR) !== 0) { + CRM_Core_Error::fatal(ts('Security: File path is not within the allowed upload directory')); + return; + } + $path = explode('/', $data); $filename = $path[count($path) - 1];
    {ts}Name{/ts}{$displayName}
    {ts}Grant Status{/ts} {$grantStatus}
    {ts}Grant Type{/ts} {$grantType}
    {ts}Grant Status{/ts} {$grantStatus|escape}
    {ts}Grant Type{/ts} {$grantType|escape}
    {ts}Application Received{/ts} {$application_received_date|crmDate}
    {ts}Grant Decision{/ts} {$decision_date|crmDate}
    {ts}Money Transferred{/ts} {$money_transfer_date|crmDate}
    {$row.sort_name}{$row.grant_status}{$row.grant_type}{$row.grant_status|escape}{$row.grant_type|escape} {$row.grant_amount_total|crmMoney} {$row.grant_amount_requested|crmMoney} {$row.grant_amount_granted|crmMoney}
    {$item.website_type} {ts}Website{/ts}{$item.url}{$item.website_type|escape} {ts}Website{/ts}{$item.url|escape}
    {$row.template_title}{$row.event_type}{$row.participant_role}{$row.template_title|escape}{$row.event_type|escape}{$row.participant_role|escape} {$row.participant_listing} {if $row.is_public eq 1}{ts}Yes{/ts} {else} {ts}No{/ts} {/if} {if $row.is_monetary eq 1}{ts}Yes{/ts} {else} {ts}No{/ts} {/if}
    {$row.id}{$row.event_type}{$row.event_type|escape} {$row.title|smarty:nodefaults|purify} {$row.contact_type} {$row.sort_name}{$row.grant_status}{$row.grant_type}{$row.grant_status|escape}{$row.grant_type|escape} {$row.grant_amount_total|crmMoney} {$row.grant_amount_granted|crmMoney} {$row.grant_application_received_date|truncate:10:''|crmDate}
    {ts}By Relationship{/ts}{$relationship}  {$owner_display_name} 
    {ts}Membership Type{/ts}{$membership_type}
    {ts}Membership Type{/ts}{$membership_type|escape}
    {ts}Status{/ts}{$status}
    {ts}Source{/ts}{$source}
    {ts}Join date{/ts}{$join_date|crmDate}
    - {$row.membership_type} + + {$row.membership_type|escape} {if $row.owner_membership_id}
    ({ts}by relationship{/ts}){/if}
    {$row.join_date|truncate:10:''|crmDate}
    {$row.id}{$row.label}{$row.label|escape} {ts}{$row.start_event}{/ts} {ts}{$row.end_event}{/ts} {if $row.is_current_member eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}
    {$row.label}{$row.label|escape} {$row.html_type} {$row.order} {if $row.is_required eq 1} {ts}Yes{/ts} {else} {ts}No{/ts} {/if}
    {$event.title}{$event.eventType}{$event.title|escape}{$event.eventType|escape} {if $event.isPublic}{ts}Yes{/ts}{else}{ts}No{/ts}{/if} {$event.startDate|crmDate}{if $event.endDate} to {$event.endDate|crmDate}{/if}
    {$contributionPage.title}{$contributionPage.type}{$contributionPage.title|escape}{$contributionPage.type|escape} {$contributionPage.startDate|crmDate}{if $contributionPage.endDate} to {$contributionPage.endDate|crmDate}{/if}
    {include file='CRM/common/modal.tpl' modalImage=$element.field_value.image}{$element.field_value.fileName}{$element.field_value.fileName|escape}{include file='CRM/common/modal.tpl' modalImage=$element.field_value.image}{$element.field_value.fileName}{$element.field_value.fileName|escape}{$element.field_value|escape}