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); } }