diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 552ef54..39ff6fd 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -16,6 +16,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\SafeStringInterface; use Drupal\Core\Datetime\DateFormatter; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Render\RenderableInterface; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\UrlGeneratorInterface; @@ -154,6 +155,7 @@ public function getFilters() { // "raw" filter and give it identifiable names. These filters should only // be used in "trans" tags. // @see TwigNodeTrans::compileString() + new \Twig_SimpleFilter('url_filter', array($this, 'urlFilter'), array('is_safe' => array('html'))), new \Twig_SimpleFilter('placeholder', [$this, 'escapePlaceholder'], array('is_safe' => array('html'), 'needs_environment' => TRUE)), // Replace twig's escape filter with our own. @@ -367,6 +369,19 @@ public function escapePlaceholder($env, $string) { } /** + * Escapes URLs to HTML and strips them of dangerous protocols. + * + * @param mixed $string + * The URL. + * + * @return null|string + * The sanitized, rendered output, or NULL if there is no valid output. + */ + public function urlFilter($string) { + return Html::escape(UrlHelper::stripDangerousProtocols($string)); + } + + /** * Overrides twig_escape_filter(). * * Replacement function for Twig's escape filter. diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php index 757b94b..6d4a6bf 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php +++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php @@ -139,6 +139,9 @@ protected function compileString(\Twig_NodeInterface $body) { case 'placeholder': $argPrefix = '%'; break; + case 'url_filter': + $argPrefix = ':'; + break; } $args = $args->getNode('node'); } diff --git a/core/modules/system/src/Tests/Theme/TwigTransTest.php b/core/modules/system/src/Tests/Theme/TwigTransTest.php index ac970cd..f5a1487 100644 --- a/core/modules/system/src/Tests/Theme/TwigTransTest.php +++ b/core/modules/system/src/Tests/Theme/TwigTransTest.php @@ -167,6 +167,12 @@ protected function assertTwigTransTags() { 'O HAI NU TXTZZZZ.', '{% trans with {"context": "Lolspeak", "langcode": "zz"} %} was successfully translated with context in specified language.' ); + + $this->assertText( + "Kittens be full 'o awe: alert(0)", + "{% trans %} Kittens are awesome: {{ 'javascript:alert(0)'|url_filter }} {% endtrans %} was succesfully escaped and translated" + ); + // Makes sure https://www.drupal.org/node/2489024 doesn't happen without // twig debug. $this->assertNoText(pi(), 'Running php code inside a Twig trans is not possible.'); @@ -248,6 +254,9 @@ protected function poFileContents($langcode) { msgid "Escaped: @string" msgstr "ESCAPEE: @string" +msgid "Kittens are awesome: :url" +msgstr "Kittens be full 'o awe: :url" + msgid "Placeholder: %string" msgstr "PLAYSHOLDR: %string" diff --git a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig index dc08db3..9f0f147 100644 --- a/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig +++ b/core/modules/system/tests/modules/twig_theme_test/templates/twig_theme_test.trans.html.twig @@ -96,3 +96,11 @@ Number I never remember: ' . print(pi()) . ' {% endtrans %} + +{# Tests URL escaping. #} +