diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php index 9081fae..a14c53d 100644 --- a/core/lib/Drupal/Component/Utility/Xss.php +++ b/core/lib/Drupal/Component/Utility/Xss.php @@ -113,6 +113,46 @@ public static function filterAdmin($string) { } /** + * Applies a very permissive XSS/HTML filter, suitable for strings that may + * contain Twig syntax, for admin-only use. + * + * Allows all tags that can be used inside an HTML body, save for scripts and + * styles. Unlike Xss::filterAdmin() this function does not remove potential + * Xss threats within Twig's default delimiters ("{%" and "%}"). + * + * @param string $string + * The string to apply the filter to. + * + * @return string + * The filtered string. + */ + public static function filterAdminTwig($string) { + $return = static::filter($string, static::$adminTags); + + // Xss::filterAdmin encodes '>' and '<' to '>' and '<'. Since these + // are both valid in Twig, we un-encode those that appear within block + // delimiters ('{%' and '%}') before handing it back to Twig for processing. + $return = preg_replace([ + '/\{% # Opening Twig statement delimiter + ([\s\S]*?) # followed by anything (ungreedy match) + > # and an escaped ">" character + ([\s\S]*?) # followed by anything (ungreedy match) + %\} # and the closing Twig statement delimiter. + /x', + '/\{% # Or: Opening Twig statement delimiter + ([\s\S]*?) # followed by anything (ungreedy match) + < # and an escaped "<" character + ([\s\S]*?) # followed by anything (ungreedy match) + %\} # and the closing Twig statement delimiter. + /x'], + ['{%$1>$2%}', '{%$1<$2%}'], + $return + ); + + return $return; + } + + /** * Processes an HTML tag. * * @param string $string diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index f17df18..3575a09 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -1285,7 +1285,7 @@ public function renderText($alter) { */ protected function renderAltered($alter, $tokens) { // Filter this right away as our substitutions are already sanitized. - $template = Xss::filterAdmin($alter['text']); + $template = Xss::filterAdminTwig($alter['text']); return $this->viewsTokenReplace($template, $tokens); } diff --git a/core/modules/views/src/Tests/Handler/FieldUnitTest.php b/core/modules/views/src/Tests/Handler/FieldUnitTest.php index 7c319d4..a6021ee 100644 --- a/core/modules/views/src/Tests/Handler/FieldUnitTest.php +++ b/core/modules/views/src/Tests/Handler/FieldUnitTest.php @@ -175,7 +175,7 @@ public function testFieldTokens() { $name_field_1->options['alter']['text'] = '{{ name_1 }} {{ name }}'; $name_field_2->options['alter']['alter_text'] = TRUE; - $name_field_2->options['alter']['text'] = '{{ name_2 }} {{ name_1 }}'; + $name_field_2->options['alter']['text'] = '{% if name_2|length > 3 %}{{ name_2 }} {{ name_1 }}{% endif %}'; foreach ($view->result as $row) { $expected_output_0 = $row->views_test_data_name; diff --git a/core/tests/Drupal/Tests/Component/Utility/XssTest.php b/core/tests/Drupal/Tests/Component/Utility/XssTest.php index 7ca4be8..b5b0234 100644 --- a/core/tests/Drupal/Tests/Component/Utility/XssTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php @@ -563,6 +563,113 @@ public function providerTestFilterXssAdminNotNormalized() { } /** + * Tests the less restrictive, admin Twig filter. + * + * @param string $value + * The value to filter. + * @param string $expected + * The expected result. + * @param string $message + * The assertion message to display upon failure. + * + * @dataProvider providerTestFilterXssAdminTwig + */ + public function testFilterXssAdminTwig($value, $expected, $message) { + $this->assertEquals(Xss::filterAdminTwig($value), $expected, $message); + } + + /** + * Data provider for testFilterXssAdminTwig(). + * + * @see testFilterXssAdminTwig + * + * @return array + * An array of arrays containing strings: + * - The value to filter. + * - The value to expect after filtering. + * - The assertion message. + */ + public function providerTestFilterXssAdminTwig() { + return array( + // Similar to Xss::filterAdmin(), ::filterAdminTwig should strip unallowed + // tags, but leave their contents. + array( + '