Fix multi-byte UFT-8 in escape('html_attr')

This commit is contained in:
Jens Svalgaard Kohrt
2018-08-16 11:25:47 +02:00
committed by Nicolas Grekas
parent 1329b5580f
commit dc3acfb7cd
3 changed files with 29 additions and 40 deletions
+1 -1
View File
@@ -28,7 +28,7 @@
}, },
"require": { "require": {
"php": "^7.0", "php": "^7.0",
"symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-ctype": "^1.8" "symfony/polyfill-ctype": "^1.8"
}, },
"require-dev": { "require-dev": {
+27 -39
View File
@@ -902,6 +902,10 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
} }
} }
if ('' === $string) {
return '';
}
if (null === $charset) { if (null === $charset) {
$charset = $env->getCharset(); $charset = $env->getCharset();
} }
@@ -953,7 +957,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
$string = iconv($charset, 'UTF-8', $string); $string = iconv($charset, 'UTF-8', $string);
} }
if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) { if (!preg_match('//u', $string)) {
throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
} }
@@ -1001,27 +1005,14 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
$string = iconv($charset, 'UTF-8', $string); $string = iconv($charset, 'UTF-8', $string);
} }
if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) { if (!preg_match('//u', $string)) {
throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
} }
$string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) { $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
$char = $matches[0]; $char = $matches[0];
// \xHH return sprintf('\\%X ', 1 === strlen($char) ? ord($char) : mb_ord($char, 'UTF-8'));
if (!isset($char[1])) {
$hex = ltrim(strtoupper(bin2hex($char)), '0');
if (0 === strlen($hex)) {
$hex = '0';
}
return '\\'.$hex.' ';
}
// \uHHHH
$char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' ';
}, $string); }, $string);
if ('UTF-8' !== $charset) { if ('UTF-8' !== $charset) {
@@ -1035,7 +1026,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
$string = iconv($charset, 'UTF-8', $string); $string = iconv($charset, 'UTF-8', $string);
} }
if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) { if (!preg_match('//u', $string)) {
throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
} }
@@ -1046,18 +1037,6 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com) * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://framework.zend.com/license/new-bsd New BSD License * @license https://framework.zend.com/license/new-bsd New BSD License
*/ */
/*
* While HTML supports far more named entities, the lowest common denominator
* has become HTML5's XML Serialisation which is restricted to the those named
* entities that XML supports. Using HTML entities would result in this error:
* XML Parsing Error: undefined entity
*/
static $entityMap = array(
34 => 'quot', /* quotation mark */
38 => 'amp', /* ampersand */
60 => 'lt', /* less-than sign */
62 => 'gt', /* greater-than sign */
);
$chr = $matches[0]; $chr = $matches[0];
$ord = ord($chr); $ord = ord($chr);
@@ -1074,23 +1053,32 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
* Check if the current character to escape has a name entity we should * Check if the current character to escape has a name entity we should
* replace it with while grabbing the hex value of the character. * replace it with while grabbing the hex value of the character.
*/ */
if (1 == strlen($chr)) { if (1 === strlen($chr)) {
$hex = strtoupper(substr('00'.bin2hex($chr), -2)); /*
} else { * While HTML supports far more named entities, the lowest common denominator
$chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8'); * has become HTML5's XML Serialisation which is restricted to the those named
$hex = strtoupper(substr('0000'.bin2hex($chr), -4)); * entities that XML supports. Using HTML entities would result in this error:
} * XML Parsing Error: undefined entity
*/
static $entityMap = array(
34 => '"', /* quotation mark */
38 => '&', /* ampersand */
60 => '<', /* less-than sign */
62 => '>', /* greater-than sign */
);
$int = hexdec($hex); if (isset($entityMap[$ord])) {
if (array_key_exists($int, $entityMap)) { return $entityMap[$ord];
return sprintf('&%s;', $entityMap[$int]); }
return sprintf('&#x%02X;', $ord);
} }
/* /*
* Per OWASP recommendations, we'll use hex entities for any other * Per OWASP recommendations, we'll use hex entities for any other
* characters where a named entity does not exist. * characters where a named entity does not exist.
*/ */
return sprintf('&#x%s;', $hex); return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8'));
}, $string); }, $string);
if ('UTF-8' !== $charset) { if ('UTF-8' !== $charset) {
+1
View File
@@ -23,6 +23,7 @@ class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase
'\'' => ''', '\'' => ''',
/* Characters beyond ASCII value 255 to unicode escape */ /* Characters beyond ASCII value 255 to unicode escape */
'Ā' => 'Ā', 'Ā' => 'Ā',
'😀' => '😀',
/* Immune chars excluded */ /* Immune chars excluded */
',' => ',', ',' => ',',
'.' => '.', '.' => '.',