Skip to content

Commit

Permalink
SuiteCRM 7.12.14 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
jack7anderson7 committed Nov 6, 2023
1 parent df71828 commit 436f421
Show file tree
Hide file tree
Showing 28 changed files with 198 additions and 266 deletions.
2 changes: 2 additions & 0 deletions ModuleInstall/ModuleScanner.php
Expand Up @@ -602,6 +602,8 @@ public function scanFile($file)
}
$contents = file_get_contents($file);
if (!$this->isPHPFile($contents)) {
$issues[] = translate('ML_INVALID_PHP_FILE', 'Administration');
$this->issues['file'][$file] = $issues;
return $issues;
}
$tokens = @token_get_all($contents);
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -2,7 +2,7 @@
<img width="180px" height="41px" src="https://suitecrm.com/wp-content/uploads/2017/12/logo.png" align="right" />
</a>

# SuiteCRM 7.12.13
# SuiteCRM 7.12.14

[![Build Status](https://travis-ci.org/salesagility/SuiteCRM.svg?branch=hotfix)](https://travis-ci.org/salesagility/SuiteCRM)
[![codecov](https://codecov.io/gh/salesagility/SuiteCRM/branch/hotfix/graph/badge.svg)](https://codecov.io/gh/salesagility/SuiteCRM/branch/hotfix)
Expand Down
2 changes: 1 addition & 1 deletion download.php
Expand Up @@ -272,7 +272,7 @@
$row['file_ext'] = pathinfo($name, PATHINFO_EXTENSION);
}

if (in_array($row['file_ext'], $allowedPreview, true)) {
if (!empty($row['file_ext']) && in_array($row['file_ext'], $allowedPreview, true)) {
$showPreview = isset($_REQUEST['preview']) && $_REQUEST['preview'] === 'yes' && $mime_type !== 'text/html';
}

Expand Down
51 changes: 23 additions & 28 deletions files.md5

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion include/Dashlets/DashletGeneric.php
Expand Up @@ -541,7 +541,7 @@ public function saveOptions($req)
}
}
if (!empty($req['dashletTitle'])) {
$options['title'] = $req['dashletTitle'];
$options['title'] = htmlentities(html_entity_decode($req['dashletTitle']));
}

// Don't save the options for myItemsOnly if we're not even showing the options.
Expand Down
2 changes: 1 addition & 1 deletion include/Dashlets/DashletGenericChart.php
Expand Up @@ -229,7 +229,7 @@ public function saveOptions(
}

if (!empty($req['dashletTitle'])) {
$options['title'] = $req['dashletTitle'];
$options['title'] = htmlentities(html_entity_decode($req['dashletTitle']));
}

$options['autoRefresh'] = empty($req['autoRefresh']) ? '0' : $req['autoRefresh'];
Expand Down
117 changes: 85 additions & 32 deletions include/HtmlSanitizer.php
Expand Up @@ -24,7 +24,7 @@ class HtmlSanitizer
/**
* SugarCleaner constructor.
*/
public function __construct()
public function __construct(array $extraConfigs = [])
{
$configurator = new \Configurator();
$sugar_config = $configurator->config;
Expand All @@ -36,29 +36,31 @@ public function __construct()
create_cache_directory("htmlclean/");
}

$config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
$config->set('Core.Encoding', 'UTF-8');
$baseConfigs = [];
$baseConfigs['HTML.Doctype'] = 'XHTML 1.0 Transitional';
$baseConfigs['Core.Encoding'] = 'UTF-8';
$hidden_tags = array('script' => true, 'style' => true, 'title' => true, 'head' => true);
$config->set('Core.HiddenElements', $hidden_tags);
$config->set('Cache.SerializerPath', sugar_cached("htmlclean"));
$config->set('URI.Base', isset($sugar_config['site_url']) ? $sugar_config['site_url'] : null);
$config->set('CSS.Proprietary', true);
$config->set('HTML.TidyLevel', 'light');
$config->set('HTML.ForbiddenElements', array('body' => true, 'html' => true));
$config->set('AutoFormat.RemoveEmpty', true);
$config->set('Cache.SerializerPermissions', 0775);
$config->set('Filter.ExtractStyleBlocks.TidyImpl', false);
$baseConfigs['Core.HiddenElements'] = $hidden_tags;
$baseConfigs['URI.Base'] = $sugar_config['site_url'] ?? null;
$baseConfigs['CSS.Proprietary'] = true;
$baseConfigs['HTML.TidyLevel'] = 'light';
$baseConfigs['HTML.ForbiddenElements'] = array('body' => true, 'html' => true);
$baseConfigs['AutoFormat.RemoveEmpty'] = true;
$baseConfigs['Cache.SerializerPermissions'] = 0775;
$baseConfigs['Filter.ExtractStyleBlocks.TidyImpl'] = false;
if (!empty($sugar_config['html_allow_objects'])) {
$config->set('HTML.SafeObject', true);
$config->set('HTML.SafeEmbed', true);
$baseConfigs['HTML.SafeObject'] = true;
$baseConfigs['HTML.SafeEmbed'] = true;
}
$config->set('Output.FlashCompat', true);
$config->set('Filter.Custom', array(new HTMLPurifierFilterXmp()));
$config->set('HTML.DefinitionID', 'Sugar HTML Def');
$config->set('HTML.DefinitionRev', 2);
$config->set('Cache.SerializerPath', sugar_cached('htmlclean/'));
$config->set('Attr.EnableID', true);
$config->set('Attr.IDPrefix', 'sugar_text_');
$baseConfigs['Output.FlashCompat'] = true;
$baseConfigs['Filter.Custom'] = array(new HTMLPurifierFilterXmp());
$baseConfigs['HTML.DefinitionID'] = 'Sugar HTML Def';
$baseConfigs['HTML.DefinitionRev'] = 2;
$baseConfigs['Cache.SerializerPath'] = sugar_cached('htmlclean/');
$baseConfigs['Attr.EnableID'] = true;
$baseConfigs['Attr.IDPrefix'] = 'sugar_text_';

$this->applyConfigs($baseConfigs, $extraConfigs, $config);

if ($def = $config->maybeGetRawHTMLDefinition()) {
$iframe = $def->addElement(
Expand Down Expand Up @@ -109,20 +111,43 @@ public static function getInstance()
* @return string clean html
*/
public static function cleanHtml($dirtyHtml, $removeHtml = false)
{
return self::getInstance()->clean($dirtyHtml, $removeHtml);
}

/**
* @param $dirtyHtml
* @param bool $isEncoded
* @return string
*/
public static function stripTags($dirtyHtml, $isEncoded = true)
{
if ($isEncoded) {
$dirtyHtml = from_html($dirtyHtml);
}
$dirtyHtml = filter_var($dirtyHtml, FILTER_SANITIZE_STRIPPED, FILTER_FLAG_NO_ENCODE_QUOTES);
return $isEncoded ? to_html($dirtyHtml) : $dirtyHtml;
}

/**
* @param string $dirtyHtml
* @param bool $removeHtml
* @return string
*/
public function clean(string $dirtyHtml, bool $removeHtml): string
{
// $encode_html previously effected the decoding process.
// we should decode regardless, just in case, the calling method passing encoded html
//Prevent that the email address in Outlook format are removed
$pattern = '/(.*)(&lt;([a-zA-Z0-9.!#$%&\'*+\=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)&gt;)(.*)/';
$replacement = '${1}<<a href="mailto:${3}">${3}</a>> ${4}';
$dirtyHtml = preg_replace($pattern, $replacement, $dirtyHtml);
$dirtyHtml = preg_replace($pattern, $replacement, $dirtyHtml);
$dirty_html_decoded = html_entity_decode($dirtyHtml);

// Re-encode html
if ($removeHtml === true) {
// remove all HTML tags
$sugarCleaner = self::getInstance();
$purifier = $sugarCleaner->purifier;
$purifier = $this->purifier;
$clean_html = $purifier->purify($dirty_html_decoded);
} else {
// encode all HTML tags
Expand All @@ -133,16 +158,44 @@ public static function cleanHtml($dirtyHtml, $removeHtml = false)
}

/**
* @param $dirtyHtml
* @param bool $isEncoded
* @return string
* @param array $baseConfigs
* @param array $extraConfigs
* @param \HTMLPurifier_Config $config
*/
public static function stripTags($dirtyHtml, $isEncoded = true)
protected function applyConfigs(array $baseConfigs, array $extraConfigs, \HTMLPurifier_Config $config): void
{
if ($isEncoded) {
$dirtyHtml = from_html($dirtyHtml);
$configKeys = array_keys($baseConfigs);
if (!empty($extraConfigs)) {
$configKeys = array_merge($configKeys, array_keys($extraConfigs));
}

foreach ($configKeys as $configKey) {
// no base config, set the custom config
if (!isset($baseConfigs[$configKey])) {
$config->set($configKey, $extraConfigs[$configKey]);
continue;
}

// no extra config, set the base config
if (!isset($extraConfigs[$configKey])) {
$config->set($configKey, $baseConfigs[$configKey]);
continue;
}

// both values are arrays, merge and set
if (is_array($baseConfigs[$configKey]) && is_array($extraConfigs[$configKey])) {
$config->set($configKey, array_merge($baseConfigs[$configKey], $extraConfigs[$configKey]));
continue;
}

// custom value does not match base value type, keep base value
if (is_array($baseConfigs[$configKey]) && !is_array($extraConfigs[$configKey])) {
$config->set($configKey, $baseConfigs[$configKey]);
continue;
}

//Override base value with custom value
$config->set($configKey, $extraConfigs[$configKey]);
}
$dirtyHtml = filter_var($dirtyHtml, FILTER_SANITIZE_STRIPPED, FILTER_FLAG_NO_ENCODE_QUOTES);
return $isEncoded ? to_html($dirtyHtml) : $dirtyHtml;
}
}
22 changes: 15 additions & 7 deletions include/utils.php
Expand Up @@ -231,7 +231,6 @@ function make_sugar_config(&$sugar_config)
'upload_dir' => $upload_dir, // this must be set!!
'upload_maxsize' => empty($upload_maxsize) ? 30000000 : $upload_maxsize,
'allowed_preview' => [
'pdf',
'gif',
'png',
'jpeg',
Expand Down Expand Up @@ -280,6 +279,10 @@ function make_sugar_config(&$sugar_config)
'min_cron_interval' => 30, // minimal interval between cron jobs
),
'strict_id_validation' => false,
'legacy_email_behaviour' => false,
'valid_imap_ports' => [
'110', '143', '993', '995'
]
);
}

Expand Down Expand Up @@ -495,7 +498,6 @@ function get_sugar_config_defaults(): array
'bmp'
],
'allowed_preview' => [
'pdf',
'gif',
'png',
'jpeg',
Expand Down Expand Up @@ -569,6 +571,10 @@ function get_sugar_config_defaults(): array
'enable' => true,
'gc_probability' => 1,
'gc_divisor' => 100,
],
'legacy_email_behaviour' => false,
'valid_imap_ports' => [
'110', '143', '993', '995'
]
];

Expand Down Expand Up @@ -2627,7 +2633,7 @@ function securexss($uncleanString)
$partialString = str_replace(array_keys($xss_cleanup), $xss_cleanup, $uncleanString);

$antiXss = new AntiXSS();
$antiXss->removeEvilAttributes(['style']);
$antiXss->removeEvilAttributes(['style', 'onerror']);

return $antiXss->xss_clean($partialString);
}
Expand All @@ -2651,21 +2657,23 @@ function securexsskey($value, $die = true)
* @param string|null $value
* @return string
*/
function purify_html(?string $value): string {
function purify_html(?string $value, array $extraOptions = []): string {

if (($value ?? '') === '') {
return '';
}

$cleanedValue = htmlentities(SugarCleaner::cleanHtml($value, true));
$sanitizer = new SuiteCRM\HtmlSanitizer($extraOptions);

$cleanedValue = htmlentities($sanitizer->clean($value, true));
$decoded = html_entity_decode($cleanedValue);
$doubleDecoded = html_entity_decode($decoded);

if (stripos($decoded, '<script>') !== false || stripos($doubleDecoded, '<script>') !== false){
$cleanedValue = '';
$doubleDecoded = '';
}

$doubleCleanedValue = htmlentities((string) SugarCleaner::cleanHtml($doubleDecoded, true));
$doubleCleanedValue = htmlentities($sanitizer->clean($doubleDecoded, true));

return $doubleCleanedValue;
}
Expand Down
11 changes: 10 additions & 1 deletion include/utils/php_zip_utils.php
Expand Up @@ -84,7 +84,16 @@ function unzip_file($zip_archive, $archive_file, $zip_dir)
}

if ($archive_file !== null) {
$res = $zip->extractTo(UploadFile::realpath($zip_dir), $archive_file);
try {
$res = $zip->extractTo(UploadFile::realpath($zip_dir), $archive_file);
} catch (ValueError $t) {
if (file_exists($zip_archive)){
LoggerManager::getLogger()->fatal(sprintf('ZIP Error(%d): Invalid file(%s). Deleting.', $res, $zip_archive));
unlink($zip_archive);
}
throw $t;
}

if ((new SplFileInfo($archive_file))->getExtension() == 'php') {
SugarCache::cleanFile(UploadFile::realpath($zip_dir).'/'.$archive_file);
}
Expand Down
4 changes: 0 additions & 4 deletions jssource/src_files/include/javascript/jquery.js

This file was deleted.

6 changes: 3 additions & 3 deletions modules/AOS_PDF_Templates/AOS_PDF_Templates.php
Expand Up @@ -38,8 +38,8 @@ public function __construct()
public function cleanBean()
{
parent::cleanBean();
$this->pdfheader = purify_html($this->pdfheader);
$this->description = purify_html($this->description);
$this->pdffooter = purify_html($this->pdffooter);
$this->pdfheader = purify_html($this->pdfheader, ['HTML.ForbiddenElements' => ['iframe' => true]]);
$this->description = purify_html($this->description, ['HTML.ForbiddenElements' => ['iframe' => true]]);
$this->pdffooter = purify_html($this->pdffooter, ['HTML.ForbiddenElements' => ['iframe' => true]]);
}
}
1 change: 1 addition & 0 deletions modules/Administration/language/en_us.lang.php
Expand Up @@ -671,6 +671,7 @@
'ML_SUITE_DZ' => 'SuiteCRM Developer Zone',
'ML_AVAIL_RESTRICTION' => 'The available restrictions and exceptions are detailed in the',
'ML_OVERRIDE_CORE_FILES' => 'Overriding of core SuiteCRM files is not allowed ',
'ML_INVALID_PHP_FILE' => 'Invalid file',
'ML_PATH_MAY_NOT_CONTAIN' => 'File path may not contain',
'ML_INVALID_ACTION_IN_MANIFEST' => 'Invalid action in your manifest:',
'ML_NO_MANIFEST' => 'This package does not contain a manifest',
Expand Down
2 changes: 1 addition & 1 deletion modules/Documents/Document.php
Expand Up @@ -268,7 +268,7 @@ public function fill_in_additional_detail_fields()

$allowedPreview = $sugar_config['allowed_preview'] ?? [];

if (in_array($row['file_ext'], $allowedPreview, true)) {
if (!empty($row['file_ext']) && in_array($row['file_ext'], $allowedPreview, true)) {
$this->show_preview = true;
}

Expand Down
6 changes: 3 additions & 3 deletions modules/Emails/Email.php
Expand Up @@ -1564,13 +1564,13 @@ public function save($check_notify = false)
$this->cc_addrs_names = $this->cleanEmails($this->cc_addrs_names);
$this->bcc_addrs_names = $this->cleanEmails($this->bcc_addrs_names);
$this->reply_to_addr = $this->cleanEmails($this->reply_to_addr);
$this->description = SugarCleaner::cleanHtml($this->description);
$this->description = SugarCleaner::cleanHtml($this->description ?? '');
if (empty($this->description_html)) {
$this->description_html = $this->description;
$this->description_html = nl2br($this->description_html);
}
$this->description_html = SugarCleaner::cleanHtml($this->description_html, true);
$this->raw_source = SugarCleaner::cleanHtml($this->raw_source, true);
$this->description_html = SugarCleaner::cleanHtml($this->description_html ?? '', true);
$this->raw_source = SugarCleaner::cleanHtml($this->raw_source ?? '', true);
$this->saveEmailText();
$this->saveEmailAddresses();

Expand Down
6 changes: 5 additions & 1 deletion modules/Home/SubpanelCreates.php
Expand Up @@ -42,7 +42,11 @@
*/

$mod_strings = return_module_language($current_language, $_REQUEST['target_module']);
$target_module = $_REQUEST['target_module']; // target class
$target_module = $_REQUEST['target_module'] ?? ''; // target class

if (empty($target_module) || !isAllowedModuleName($target_module)) {
throw new InvalidArgumentException('Invalid target_module');
}

if (file_exists('modules/'. $_REQUEST['target_module'] . '/EditView.php')) {
$tpl = $_REQUEST['tpl'];
Expand Down

0 comments on commit 436f421

Please sign in to comment.