mirror of
https://github.com/salesagility/SuiteCRM.git
synced 2025-02-05 22:49:44 +00:00
2c48735fbe
# Conflicts: # README.md # composer.lock # data/BeanFactory.php # files.md5 # modules/AOD_Index/AOD_Index.php # modules/AOR_Conditions/conditionLines.php # modules/AOW_Conditions/conditionLines.php # modules/Activities/SetAcceptStatus.php # modules/Administration/views/view.backups.php # modules/Calendar/controller.php # modules/Campaigns/WizardCampaignSave.php # modules/Contacts/AcceptDecline.php # modules/Emails/Compose.php # modules/Opportunities/OpportunityFormBase.php # modules/Project/chart.php # modules/SavedSearch/index.php # service/v3_1/SugarWebServiceUtilv3_1.php # soap/SoapPortalHelper.php # suitecrm_version.php # tests/unit/phpunit/data/SugarBeanTest.php # tests/unit/phpunit/include/SugarFolders/SugarFolderTest.php # tests/unit/phpunit/modules/AOS_Products/AOS_ProductsTest.php # themes/SuiteP/css/Dawn/style.css # themes/SuiteP/css/Day/style.css # themes/SuiteP/css/Dusk/style.css # themes/SuiteP/css/Night/style.css
148 lines
4.9 KiB
PHP
148 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace SuiteCRM;
|
|
|
|
/**
|
|
* Class SugarCleaner
|
|
* @package SuiteCRM
|
|
* Html Sanitizer
|
|
*/
|
|
class HtmlSanitizer
|
|
{
|
|
/**
|
|
* Singleton instance
|
|
* @var HtmlSanitizer
|
|
*/
|
|
private static $instance;
|
|
|
|
/**
|
|
* HTMLPurifier instance
|
|
* @var \HTMLPurifier
|
|
*/
|
|
protected $purifier;
|
|
|
|
/**
|
|
* SugarCleaner constructor.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$configurator = new \Configurator();
|
|
$sugar_config = $configurator->config;
|
|
|
|
|
|
$config = \HTMLPurifier_Config::createDefault();
|
|
|
|
if (!is_dir(sugar_cached("htmlclean"))) {
|
|
create_cache_directory("htmlclean/");
|
|
}
|
|
|
|
$config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
|
|
$config->set('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);
|
|
if (!empty($sugar_config['html_allow_objects'])) {
|
|
$config->set('HTML.SafeObject', true);
|
|
$config->set('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_');
|
|
|
|
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)
|
|
{
|
|
// $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
|
|
$sugarCleaner = self::getInstance();
|
|
$purifier = $sugarCleaner->purifier;
|
|
$clean_html = $purifier->purify($dirty_html_decoded);
|
|
} else {
|
|
// encode all HTML tags
|
|
$clean_html = $dirty_html_decoded;
|
|
}
|
|
|
|
return $clean_html;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
}
|
|
}
|