getConfig('DBVersion')< 172) // Permission::model exist only after 172 DB version
return $this->xssfilter=($this->xssfilter && Yii::app()->getConfig('filterxsshtml'));
$this->xssfilter=($this->xssfilter && Yii::app()->getConfig('filterxsshtml') && !Permission::model()->hasGlobalPermission('superadmin','read'));
}
protected function validateAttribute($object,$attribute)
{
if($this->xssfilter)
{
$object->$attribute=$this->xssFilter($object->$attribute);
if($this->isUrl)
{
$object->$attribute=str_replace('javascript:','',html_entity_decode($object->$attribute, ENT_QUOTES, "UTF-8"));
}
}
// Note that URL checking only checks basic URL properties. As a URL can contain EM expression there needs to be a lot of freedom.
if($this->isUrl)
{
if ($object->$attribute== 'http://' || $object->$attribute=='https://') {$object->$attribute="";}
$object->$attribute=html_entity_decode($object->$attribute, ENT_QUOTES, "UTF-8");
}
if($this->isLanguage)
{
$object->$attribute=$this->languageFilter($object->$attribute);
}
if($this->isLanguageMulti)
{
$object->$attribute=$this->multiLanguageFilter($object->$attribute);
}
}
/**
* Remove some empty characters put by CK editor
* Did we need to do if user don't use inline HTML editor ?
*
* @param string $value
*/
public function fixCKeditor($value)
{
// Actually don't use it in model : model apply too when import : needed or not ?
$value = str_replace('
','',$value);
if ($value == "
" || $value == " " || $value == " ")
{
$value = "";
}
if (preg_match("/^[\s]+$/",$value))
{
$value='';
}
if ($value == "\n")
{
$value = "";
}
if (trim($value) == " " || trim($value)=='')
{ // chrome adds a single element to empty fckeditor fields
$value = "";
}
return $value;
}
/**
* Remove any script or dangerous HTML
*
* @param string $value
*/
public function xssFilter($value)
{
$filter = new CHtmlPurifier();
$filter->options = array(
'AutoFormat.RemoveEmpty'=>false,
'Core.NormalizeNewlines'=>false,
'CSS.AllowTricky'=>true, // Allow display:none; (and other)
'HTML.SafeObject'=>true, // To allow including youtube
'Output.FlashCompat'=>true,
'Attr.EnableID'=>true, // Allow to set id
'Attr.AllowedFrameTargets'=>array('_blank','_self'),
'URI.AllowedSchemes'=>array(
'http' => true,
'https' => true,
'mailto' => true,
'ftp' => true,
'nntp' => true,
'news' => true,
)
);
// To allow script BUT purify : HTML.Trusted=true (plugin idea for admin or without XSS filtering ?)
// to enable video or something else we must use the config object of HTML-Purifier
$config = $filter->getPurifier()->config;
// enable video
$config->set('HTML.DefinitionID', 'html5-definitions');
$def = $config->maybeGetRawHTMLDefinition();
$max = $config->get('HTML.MaxImgLength');
if ($def) {
$def->addElement(
'video', // name
'Inline', // content set
'Flow', // allowed children
'Common', // attribute collection
array( // attributes
'src' => 'URI',
'id' => 'Text',
'poster' => 'Text',
'width' => 'Pixels#' . $max,
'height' => 'Pixels#' . $max,
'controls' => 'Bool#controls',
'autobuffer' => 'Bool#autobuffer',
'autoplay' => 'Bool#autoplay',
'loop' => 'Bool#loop',
'muted' => 'Bool#muted'
)
);
$def->addElement(
'source', // name
'Inline', // content set
'Empty', // allowed children
null, // attribute collection
array( // attributes
'src*' => 'URI',
'type' => 'Enum#video/mp4,video/webm',
)
);
}
/** Start to get complete filtered value with url decode {QCODE} (bug #09300). This allow only question number in url, seems OK with XSS protection **/
$sFiltered=preg_replace('#%7B([a-zA-Z0-9\.]*)%7D#','{$1}',$filter->purify($value));
Yii::import('application.helpers.expressions.em_core_helper');// Already imported in em_manager_helper.php ?
$oExpressionManager= new ExpressionManager;
/** We get 2 array : one filtered, other unfiltered **/
$aValues=$oExpressionManager->asSplitStringOnExpressions($value);// Return array of array : 0=>the string,1=>string length,2=>string type (STRING or EXPRESSION)
$aFilteredValues=$oExpressionManager->asSplitStringOnExpressions($sFiltered);// Same but for the filtered string
$bCountIsOk=count($aValues)==count($aFilteredValues);
/** Construction of new string with unfiltered EM and filtered HTML **/
$sNewValue="";
foreach($aValues as $key=>$aValue){
if($aValue[2]=="STRING")
$sNewValue.=$bCountIsOk ? $aFilteredValues[$key][0]:$filter->purify($aValue[0]);// If EM is broken : can throw invalid $key
else
{
$sExpression=trim($aValue[0], '{}');
$sNewValue.="{";
$aParsedExpressions=$oExpressionManager->Tokenize($sExpression,true);
foreach($aParsedExpressions as $aParsedExpression)
{
if($aParsedExpression[2]=='DQ_STRING')
$sNewValue.="\"".$filter->purify($aParsedExpression[0])."\""; // This disallow complex HTML construction with XSS
elseif($aParsedExpression[2]=='SQ_STRING')
$sNewValue.="'".$filter->purify($aParsedExpression[0])."'";
else
$sNewValue.=$aParsedExpression[0];
}
$sNewValue.="}";
}
}
gc_collect_cycles(); // To counter a high memory usage of HTML-Purifier
return $sNewValue;
}
/**
* Defines the customs validation rule for language string
*
* @param mixed $value
*/
public function languageFilter($value)
{
// Maybe use the array of language ?
return preg_replace('/[^a-z0-9-]/i', '', $value);
}
/**
* Defines the customs validation rule for multi language string
*
* @param mixed $value
*/
public function multiLanguageFilter($value)
{
$aValue=explode(" ",trim($value));
$aValue=array_map("sanitize_languagecode",$aValue);
return implode(" ",$aValue);
}
}