mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-08-29 01:10:42 +08:00
c009bb4487 SuiteCRM 7.14.6 Release a501e2a494 Fix format decimals on email template 9388fcce0a Fix Mozaik editor on campaigns 23060cd519 Fix tags in saving dropdown 98183bb40f Fix #10421 - Studio | Layout Editor CSS broken 376cdd86fa Fix #10503 - Item label in dropdown list is not displayed if it contains '<' character c96182239d Fix #10099 - Workflow action create record on CRON set incorrect date field values from related entity 228c549390 Fix #9036 - change log level according it message 1ead013a45 Fix #9737 - Fix issue with campaigns displaying a blank email template 48a86eef51 Fix #10450 - Add check for empty value before searching array 1cc6fc8b54 Fix #10455 - Add Independent scrollbars for Toolbox and Layout within Studio Layout Editor 9c8da04bbf Fix #10488 - Expand the number of filters in message queue views 9158c58f26 Fix #9261 - Use decimal symbol configured in system and user 4a40068018 Fix #10445 - Calendar: Calls still show even using Settings: 'Show Calls' = NO e3610fa635 Fix #507 - Workflows - Calculated Fields - related field won't show up as parameter bfde626897 Fix #8332 - Recipient Type [Language] e97708d29c Fix #10520 - Discount Calculation incorrect when changing Currency 171a9d2a0f Fix #5287 - Change secret field is too short git-subtree-dir: public/legacy git-subtree-split: c009bb448753e2dcc6deefe258c30c00395cbd07
206 lines
6.8 KiB
PHP
206 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace SuiteCRM;
|
|
|
|
/**
|
|
* Class SugarCleaner
|
|
* @package SuiteCRM
|
|
* Html Sanitizer
|
|
*/
|
|
#[\AllowDynamicProperties]
|
|
class HtmlSanitizer
|
|
{
|
|
/**
|
|
* Singleton instance
|
|
* @var HtmlSanitizer
|
|
*/
|
|
private static $instance;
|
|
|
|
/**
|
|
* HTMLPurifier instance
|
|
* @var \HTMLPurifier
|
|
*/
|
|
protected $purifier;
|
|
|
|
/**
|
|
* SugarCleaner constructor.
|
|
*/
|
|
public function __construct(array $extraConfigs = [])
|
|
{
|
|
$configurator = new \Configurator();
|
|
$sugar_config = $configurator->config;
|
|
|
|
|
|
$config = \HTMLPurifier_Config::createDefault();
|
|
|
|
if (!is_dir(sugar_cached("htmlclean"))) {
|
|
create_cache_directory("htmlclean/");
|
|
}
|
|
|
|
$baseConfigs = [];
|
|
$baseConfigs['HTML.Doctype'] = 'XHTML 1.0 Transitional';
|
|
$baseConfigs['Core.Encoding'] = 'UTF-8';
|
|
$hidden_tags = array('script' => true, 'style' => true, 'title' => true, 'head' => true);
|
|
$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'])) {
|
|
$baseConfigs['HTML.SafeObject'] = true;
|
|
$baseConfigs['HTML.SafeEmbed'] = true;
|
|
}
|
|
$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(
|
|
'iframe',
|
|
'Flow',
|
|
'Optional: #PCDATA | Flow | Block',
|
|
'Core',
|
|
array(
|
|
'src*' => 'URI',
|
|
'frameborder' => 'Enum#0,1',
|
|
'marginwidth' => 'Pixels',
|
|
'marginheight' => 'Pixels',
|
|
'scrolling' => 'Enum#|yes,no,auto',
|
|
'align' => 'Enum#top,middle,bottom,left,right,center',
|
|
'height' => 'Length',
|
|
'width' => 'Length',
|
|
)
|
|
);
|
|
|
|
$iframe->excludes = array('iframe');
|
|
}
|
|
|
|
/** @var \HTMLPurifier_URIDefinition $uri */
|
|
$uri = $config->getDefinition('URI');
|
|
$uri->addFilter(new URIFilter(), $config);
|
|
\HTMLPurifier_URISchemeRegistry::instance()->register('cid', new HTMLPurifierURISchemeCid());
|
|
|
|
$this->purifier = new \HTMLPurifier($config);
|
|
}
|
|
|
|
/**
|
|
* Get cleaner instance
|
|
* @return HtmlSanitizer
|
|
*/
|
|
public static function getInstance()
|
|
{
|
|
if (self::$instance === null) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Clean string from potential XSS problems
|
|
* @param string $dirtyHtml
|
|
* @param bool $removeHtml - remove encoded html
|
|
* @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);
|
|
}
|
|
|
|
if (preg_match('/([a-z]+)(?![^>]*\/>)[^>]*/', $dirtyHtml)) {
|
|
$dirtyHtml = strip_tags($dirtyHtml);
|
|
}
|
|
|
|
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 = '/(.*)(<([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])?)*)>)(.*)/';
|
|
$replacement = '${1}<<a href="mailto:${3}">${3}</a>> ${4}';
|
|
$dirtyHtml = preg_replace($pattern, $replacement, $dirtyHtml);
|
|
$dirty_html_decoded = html_entity_decode($dirtyHtml);
|
|
|
|
// Re-encode html
|
|
if ($removeHtml === true) {
|
|
// remove all HTML tags
|
|
$purifier = $this->purifier;
|
|
$clean_html = $purifier->purify($dirty_html_decoded);
|
|
} else {
|
|
// encode all HTML tags
|
|
$clean_html = $dirty_html_decoded;
|
|
}
|
|
|
|
return $clean_html;
|
|
}
|
|
|
|
/**
|
|
* @param array $baseConfigs
|
|
* @param array $extraConfigs
|
|
* @param \HTMLPurifier_Config $config
|
|
*/
|
|
protected function applyConfigs(array $baseConfigs, array $extraConfigs, \HTMLPurifier_Config $config): void
|
|
{
|
|
$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]);
|
|
}
|
|
}
|
|
}
|