diff --git a/application/core/LSYii_XssValidator.php b/application/core/LSYii_XssValidator.php new file mode 100644 index 00000000000..0d3bd964056 --- /dev/null +++ b/application/core/LSYii_XssValidator.php @@ -0,0 +1,122 @@ +options = array( + 'AutoFormat.RemoveEmpty'=>false, + 'Core.NormalizeNewlines'=>false, + 'CSS.AllowTricky'=>$this->allowCSS, // 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 ?) + + /** 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', true); // 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 .= "\"".(string) $filter->purify($aParsedExpression[0])."\""; // This disallow complex HTML construction with XSS + } elseif ($aParsedExpression[2] == 'SQ_STRING') { + $sNewValue .= "'".(string) $filter->purify($aParsedExpression[0])."'"; + } else { + $sNewValue .= $aParsedExpression[0]; + } + } + $sNewValue .= "}"; + } + } + gc_collect_cycles(); // To counter a high memory usage of HTML-Purifier + return $sNewValue; + } + + protected function validateAttribute($object, $attribute) + { + if($this->rmnewlines) { + $object->$attribute = preg_replace("/(\r?\n\r?)/","", $object->$attribute); + } + + if ($this->allowHTML) { + $object->$attribute = $this->filterHTML($object->$attribute); + } else { + $object->$attribute = htmlentities(strip_tags($object->$attribute)); + } + + if($this->trim) { + $object->$attribute = trim($object->$attribute); + } + } +} diff --git a/application/models/TokenDynamic.php b/application/models/TokenDynamic.php index 0429396c9c6..75a53c6e54b 100644 --- a/application/models/TokenDynamic.php +++ b/application/models/TokenDynamic.php @@ -100,7 +100,8 @@ public function primaryKey() public function rules() { return array( - array('token', 'unique', 'allowEmpty'=>true), // 'caseSensitive'=>false only for mySql + array('firstname, lastname', 'LSYii_XssValidator', 'allowHTML' => false), + array('token', 'unique', 'allowEmpty'=>true), array('remindercount', 'numerical', 'integerOnly'=>true, 'allowEmpty'=>true), array('email', 'filter', 'filter'=>'trim'), array('email', 'LSYii_EmailIDNAValidator', 'allowEmpty'=>true, 'allowMultiple'=>true, 'except'=>'allowinvalidemail'), diff --git a/application/views/admin/token/tokenform.php b/application/views/admin/token/tokenform.php index 97b40d7ccce..299ea5d61c8 100644 --- a/application/views/admin/token/tokenform.php +++ b/application/views/admin/token/tokenform.php @@ -194,32 +194,47 @@ -
- + 'form-control', 'size' => '50', - 'maxlength' => '320' + 'maxlength' => '320', + 'pattern' => "^".$emailPattern."(;".$emailPattern.")*$" ]);?> + + +
- + - -
+ +
-
+
'form-control', 'size' => '50', 'maxlength' => '320', - 'placeholder' => 'OK' + 'placeholder' => 'OK', + 'readonly' => true, + 'data-toggle' => "tooltip", + "title" => gT("This field is to record the delivery state. Normally the system will handle this automatically") ]);?> + + +
-
+
- -
+ +
- - " /> - -
+ " /> +
+ +