From 5c7b080b314165bdacee35d5b3e363be04c008be Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Wed, 21 Mar 2018 23:48:18 +0100 Subject: [PATCH 1/4] Add JSON escape strategy --- CHANGELOG | 2 +- lib/Twig/Extension/Core.php | 22 +++++++++++++---- test/Twig/Tests/EnvironmentTest.php | 2 +- test/Twig/Tests/Fixtures/autoescape/name.test | 2 +- .../Fixtures/filters/escape_javascript.test | 2 +- .../Tests/Fixtures/filters/force_escape.test | 2 +- .../Fixtures/tags/autoescape/functions.test | 2 +- .../tags/autoescape/strategy.legacy.test | 2 +- .../Fixtures/tags/autoescape/strategy.test | 2 +- .../Tests/Fixtures/tags/autoescape/type.test | 10 ++++---- test/Twig/Tests/escapingTest.php | 24 +++++++++++-------- 11 files changed, 45 insertions(+), 27 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5d869ca2bea..98c031bd304 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ * 1.35.4 (2018-XX-XX) - * n/a + * "js" filter now produces valid JSON * 1.35.3 (2018-03-20) diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index 6e5569a28f6..0e35e36ed31 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -1040,7 +1040,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', case 'js': // escape all non-alphanumeric characters - // into their \xHH or \uHHHH representations + // into their \x or \uHHHH representations if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, 'UTF-8', $charset); } @@ -1152,9 +1152,23 @@ function _twig_escape_js_callback($matches) { $char = $matches[0]; - // \xHH - if (!isset($char[1])) { - return '\\x'.strtoupper(substr('00'.bin2hex($char), -2)); + /* + * A few characters have short escape sequences in JSON and JavaScript. + * 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 diff --git a/test/Twig/Tests/EnvironmentTest.php b/test/Twig/Tests/EnvironmentTest.php index ca9f2cf8ace..dd8dac9ad6d 100644 --- a/test/Twig/Tests/EnvironmentTest.php +++ b/test/Twig/Tests/EnvironmentTest.php @@ -61,7 +61,7 @@ public function testAutoescapeOption() )); $this->assertEquals('foo<br/ > foo<br/ >', $twig->render('html', array('foo' => 'foo
'))); - $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo
'))); + $this->assertEquals('foo\u003Cbr\/\u0020\u003E foo\u003Cbr\/\u0020\u003E', $twig->render('js', array('bar' => 'foo
'))); } public function escapingStrategyCallback($name) diff --git a/test/Twig/Tests/Fixtures/autoescape/name.test b/test/Twig/Tests/Fixtures/autoescape/name.test index 04299bed356..98e89399aec 100644 --- a/test/Twig/Tests/Fixtures/autoescape/name.test +++ b/test/Twig/Tests/Fixtures/autoescape/name.test @@ -17,6 +17,6 @@ return array('br' => '
') return array('autoescape' => 'name') --EXPECT-- <br /> -\x3Cbr\x20\x2F\x3E +\u003Cbr\u0020\/\u003E <br />
diff --git a/test/Twig/Tests/Fixtures/filters/escape_javascript.test b/test/Twig/Tests/Fixtures/filters/escape_javascript.test index 647147a439d..8e7278119a0 100644 --- a/test/Twig/Tests/Fixtures/filters/escape_javascript.test +++ b/test/Twig/Tests/Fixtures/filters/escape_javascript.test @@ -5,4 +5,4 @@ --DATA-- return array() --EXPECT-- -\u00E9\x20\u265C\x20\uD834\uDF06 +\u00E9\u0020\u265C\u0020\uD834\uDF06 diff --git a/test/Twig/Tests/Fixtures/filters/force_escape.test b/test/Twig/Tests/Fixtures/filters/force_escape.test index 85a9b7172bd..eb9cba7cf44 100644 --- a/test/Twig/Tests/Fixtures/filters/force_escape.test +++ b/test/Twig/Tests/Fixtures/filters/force_escape.test @@ -14,5 +14,5 @@ return array() --EXPECT-- foo<br /> -\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A +\u0020\u0020\u0020\u0020foo\u003Cbr\u0020\/\u003E\n foo
diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/functions.test b/test/Twig/Tests/Fixtures/tags/autoescape/functions.test index ce7ea789ebb..653c41b8ef1 100644 --- a/test/Twig/Tests/Fixtures/tags/autoescape/functions.test +++ b/test/Twig/Tests/Fixtures/tags/autoescape/functions.test @@ -80,4 +80,4 @@ unsafe_br()|escape autoescape js safe_br -\x3Cbr\x20\x2F\x3E +\u003Cbr\u0020\/\u003E diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test index bbf1356e731..c3f8eddfac8 100644 --- a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test +++ b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test @@ -7,5 +7,5 @@ --DATA-- return array('var' => '
"') --EXPECT-- -\x3Cbr\x20\x2F\x3E\x22 +\u003Cbr\u0020\/\u003E\u0022 <br />" diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test index e496f60818e..5b69449c24a 100644 --- a/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test +++ b/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test @@ -7,5 +7,5 @@ --DATA-- return array('var' => '
"') --EXPECT-- -\x3Cbr\x20\x2F\x3E\x22 +\u003Cbr\u0020\/\u003E\u0022 <br />" diff --git a/test/Twig/Tests/Fixtures/tags/autoescape/type.test b/test/Twig/Tests/Fixtures/tags/autoescape/type.test index 4f415201db1..1250f0db199 100644 --- a/test/Twig/Tests/Fixtures/tags/autoescape/type.test +++ b/test/Twig/Tests/Fixtures/tags/autoescape/type.test @@ -44,15 +44,15 @@ return array('msg' => "<>\n'\"") 1. autoescape 'html' |escape('js') - + 2. autoescape 'html' |escape('js') - + 3. autoescape 'js' |escape('js') - + 4. no escape @@ -61,9 +61,9 @@ return array('msg' => "<>\n'\"") 5. |escape('js')|escape('html') - + 6. autoescape 'html' |escape('js')|escape('html') - + diff --git a/test/Twig/Tests/escapingTest.php b/test/Twig/Tests/escapingTest.php index 9b98dddcf13..9c2e1203825 100644 --- a/test/Twig/Tests/escapingTest.php +++ b/test/Twig/Tests/escapingTest.php @@ -51,13 +51,15 @@ class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase protected $jsSpecialChars = array( /* HTML special chars - escape without exception to hex */ - '<' => '\\x3C', - '>' => '\\x3E', - '\'' => '\\x27', - '"' => '\\x22', - '&' => '\\x26', + '<' => '\\u003C', + '>' => '\\u003E', + '\'' => '\\u0027', + '"' => '\\u0022', + '&' => '\\u0026', + '/' => '\\/', /* Characters beyond ASCII value 255 to unicode escape */ 'Ā' => '\\u0100', + '😀' => '\\uD83D\\uDE00', /* Immune chars excluded */ ',' => ',', '.' => '.', @@ -70,12 +72,14 @@ class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase '0' => '0', '9' => '9', /* Basic control characters and null */ - "\r" => '\\x0D', - "\n" => '\\x0A', - "\t" => '\\x09', - "\0" => '\\x00', + "\r" => '\r', + "\n" => '\n', + "\x08" => '\b', + "\t" => '\t', + "\x0C" => '\f', + "\0" => '\\u0000', /* Encode spaces for quoteless attribute protection */ - ' ' => '\\x20', + ' ' => '\\u0020', ); protected $urlSpecialChars = array( From 555d01f3278eaaa46463855d340bf19947b5b7b4 Mon Sep 17 00:00:00 2001 From: "Andrey F. Mindubaev" Date: Mon, 26 Mar 2018 09:53:48 +0300 Subject: [PATCH 2/4] Small optimization for Twig_NodeTraverser::traverseForVisitor --- lib/Twig/NodeTraverser.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Twig/NodeTraverser.php b/lib/Twig/NodeTraverser.php index 787629ce344..f00a0bf5c4e 100644 --- a/lib/Twig/NodeTraverser.php +++ b/lib/Twig/NodeTraverser.php @@ -70,8 +70,10 @@ protected function traverseForVisitor(Twig_NodeVisitorInterface $visitor, Twig_N $node = $visitor->enterNode($node, $this->env); foreach ($node as $k => $n) { - if (false !== $n = $this->traverseForVisitor($visitor, $n)) { - $node->setNode($k, $n); + if (false !== $m = $this->traverseForVisitor($visitor, $n)) { + if ($m !== $n) { + $node->setNode($k, $m); + } } else { $node->removeNode($k); } From 50d990e23de7d6260e5636dcb72050c74e9a73d2 Mon Sep 17 00:00:00 2001 From: AY_W Date: Sat, 31 Mar 2018 20:48:55 +0300 Subject: [PATCH 3/4] Fix cache update after uploading the template file (when auto-update is enabled). If in the process of uploading the template file, a render() is called, the partially uploaded file gets into the cache and after the template file is fully uploaded, the cache is not updated anymore. --- lib/Twig/Loader/Filesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Twig/Loader/Filesystem.php b/lib/Twig/Loader/Filesystem.php index 9149bc3ea46..4e8be0d53e5 100644 --- a/lib/Twig/Loader/Filesystem.php +++ b/lib/Twig/Loader/Filesystem.php @@ -175,7 +175,7 @@ public function exists($name) public function isFresh($name, $time) { - return filemtime($this->findTemplate($name)) <= $time; + return filemtime($this->findTemplate($name)) < $time; } protected function findTemplate($name) From 28690681d12004733120539f6336a215e358f983 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 20 Apr 2018 07:23:43 +0200 Subject: [PATCH 4/4] fixed website URL --- README.rst | 2 +- composer.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 81737b0b2c2..f33ea336d86 100644 --- a/README.rst +++ b/README.rst @@ -12,4 +12,4 @@ More Information Read the `documentation`_ for more information. -.. _documentation: http://twig.sensiolabs.org/documentation +.. _documentation: https://twig.symfony.com/documentation diff --git a/composer.json b/composer.json index 7dab3034e9f..5f5595d4f6b 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "library", "description": "Twig, the flexible, fast, and secure template language for PHP", "keywords": ["templating"], - "homepage": "http://twig.sensiolabs.org", + "homepage": "https://twig.symfony.com", "license": "BSD-3-Clause", "authors": [ { @@ -14,7 +14,7 @@ }, { "name": "Twig Team", - "homepage": "http://twig.sensiolabs.org/contributors", + "homepage": "https://twig.symfony.com/contributors", "role": "Contributors" }, {