Add JSON escape strategy

This commit is contained in:
Christian Schmidt
2018-03-21 23:48:18 +01:00
committed by Fabien Potencier
parent c35ad8c4e3
commit 5c7b080b31
11 changed files with 45 additions and 27 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
* 1.35.4 (2018-XX-XX) * 1.35.4 (2018-XX-XX)
* n/a * "js" filter now produces valid JSON
* 1.35.3 (2018-03-20) * 1.35.3 (2018-03-20)
+18 -4
View File
@@ -1040,7 +1040,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
case 'js': case 'js':
// escape all non-alphanumeric characters // escape all non-alphanumeric characters
// into their \xHH or \uHHHH representations // into their \x or \uHHHH representations
if ('UTF-8' !== $charset) { if ('UTF-8' !== $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset); $string = twig_convert_encoding($string, 'UTF-8', $charset);
} }
@@ -1152,9 +1152,23 @@ function _twig_escape_js_callback($matches)
{ {
$char = $matches[0]; $char = $matches[0];
// \xHH /*
if (!isset($char[1])) { * A few characters have short escape sequences in JSON and JavaScript.
return '\\x'.strtoupper(substr('00'.bin2hex($char), -2)); * Escape sequences supported only by JavaScript, not JSON, are ommitted.
* \" is also supported but omitted, because the resulting string is not HTML safe.
*/
static $shortMap = array(
'\\' => '\\\\',
'/' => '\\/',
"\x08" => '\b',
"\x0C" => '\f',
"\x0A" => '\n',
"\x0D" => '\r',
"\x09" => '\t',
);
if (isset($shortMap[$char])) {
return $shortMap[$char];
} }
// \uHHHH // \uHHHH
+1 -1
View File
@@ -61,7 +61,7 @@ class Twig_Tests_EnvironmentTest extends \PHPUnit\Framework\TestCase
)); ));
$this->assertEquals('foo&lt;br/ &gt; foo&lt;br/ &gt;', $twig->render('html', array('foo' => 'foo<br/ >'))); $this->assertEquals('foo&lt;br/ &gt; foo&lt;br/ &gt;', $twig->render('html', array('foo' => 'foo<br/ >')));
$this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo<br/ >'))); $this->assertEquals('foo\u003Cbr\/\u0020\u003E foo\u003Cbr\/\u0020\u003E', $twig->render('js', array('bar' => 'foo<br/ >')));
} }
public function escapingStrategyCallback($name) public function escapingStrategyCallback($name)
@@ -17,6 +17,6 @@ return array('br' => '<br />')
return array('autoescape' => 'name') return array('autoescape' => 'name')
--EXPECT-- --EXPECT--
&lt;br /&gt; &lt;br /&gt;
\x3Cbr\x20\x2F\x3E \u003Cbr\u0020\/\u003E
&lt;br /&gt; &lt;br /&gt;
<br /> <br />
@@ -5,4 +5,4 @@
--DATA-- --DATA--
return array() return array()
--EXPECT-- --EXPECT--
\u00E9\x20\u265C\x20\uD834\uDF06 \u00E9\u0020\u265C\u0020\uD834\uDF06
@@ -14,5 +14,5 @@
return array() return array()
--EXPECT-- --EXPECT--
foo&lt;br /&gt; foo&lt;br /&gt;
\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A \u0020\u0020\u0020\u0020foo\u003Cbr\u0020\/\u003E\n
foo<br /> foo<br />
@@ -80,4 +80,4 @@ unsafe_br()|escape
autoescape js autoescape js
safe_br safe_br
\x3Cbr\x20\x2F\x3E \u003Cbr\u0020\/\u003E
@@ -7,5 +7,5 @@
--DATA-- --DATA--
return array('var' => '<br />"') return array('var' => '<br />"')
--EXPECT-- --EXPECT--
\x3Cbr\x20\x2F\x3E\x22 \u003Cbr\u0020\/\u003E\u0022
&lt;br /&gt;&quot; &lt;br /&gt;&quot;
@@ -7,5 +7,5 @@
--DATA-- --DATA--
return array('var' => '<br />"') return array('var' => '<br />"')
--EXPECT-- --EXPECT--
\x3Cbr\x20\x2F\x3E\x22 \u003Cbr\u0020\/\u003E\u0022
&lt;br /&gt;&quot; &lt;br /&gt;&quot;
@@ -44,15 +44,15 @@ return array('msg' => "<>\n'\"")
1. autoescape 'html' |escape('js') 1. autoescape 'html' |escape('js')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a> <a onclick="alert(&quot;\u003C\u003E\n\u0027\u0022&quot;)"></a>
2. autoescape 'html' |escape('js') 2. autoescape 'html' |escape('js')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a> <a onclick="alert(&quot;\u003C\u003E\n\u0027\u0022&quot;)"></a>
3. autoescape 'js' |escape('js') 3. autoescape 'js' |escape('js')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a> <a onclick="alert(&quot;\u003C\u003E\n\u0027\u0022&quot;)"></a>
4. no escape 4. no escape
@@ -61,9 +61,9 @@ return array('msg' => "<>\n'\"")
5. |escape('js')|escape('html') 5. |escape('js')|escape('html')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a> <a onclick="alert(&quot;\u003C\u003E\n\u0027\u0022&quot;)"></a>
6. autoescape 'html' |escape('js')|escape('html') 6. autoescape 'html' |escape('js')|escape('html')
<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a> <a onclick="alert(&quot;\u003C\u003E\n\u0027\u0022&quot;)"></a>
+14 -10
View File
@@ -51,13 +51,15 @@ class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase
protected $jsSpecialChars = array( protected $jsSpecialChars = array(
/* HTML special chars - escape without exception to hex */ /* HTML special chars - escape without exception to hex */
'<' => '\\x3C', '<' => '\\u003C',
'>' => '\\x3E', '>' => '\\u003E',
'\'' => '\\x27', '\'' => '\\u0027',
'"' => '\\x22', '"' => '\\u0022',
'&' => '\\x26', '&' => '\\u0026',
'/' => '\\/',
/* Characters beyond ASCII value 255 to unicode escape */ /* Characters beyond ASCII value 255 to unicode escape */
'Ā' => '\\u0100', 'Ā' => '\\u0100',
'😀' => '\\uD83D\\uDE00',
/* Immune chars excluded */ /* Immune chars excluded */
',' => ',', ',' => ',',
'.' => '.', '.' => '.',
@@ -70,12 +72,14 @@ class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase
'0' => '0', '0' => '0',
'9' => '9', '9' => '9',
/* Basic control characters and null */ /* Basic control characters and null */
"\r" => '\\x0D', "\r" => '\r',
"\n" => '\\x0A', "\n" => '\n',
"\t" => '\\x09', "\x08" => '\b',
"\0" => '\\x00', "\t" => '\t',
"\x0C" => '\f',
"\0" => '\\u0000',
/* Encode spaces for quoteless attribute protection */ /* Encode spaces for quoteless attribute protection */
' ' => '\\x20', ' ' => '\\u0020',
); );
protected $urlSpecialChars = array( protected $urlSpecialChars = array(