diff --git a/core/includes/common.inc b/core/includes/common.inc index 1980f2f..fbfc99a 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -413,7 +413,9 @@ function drupal_add_feed($url = NULL, $title = '') { */ function drupal_get_feeds($delimiter = "\n") { $feeds = drupal_add_feed(); - return implode($feeds, $delimiter); + $string = implode($feeds, $delimiter); + $GLOBALS['safe_strings'][$string] = TRUE; + return $string; } /** @@ -1366,7 +1368,9 @@ function l($text, $path, array $options = array()) { // Sanitize the link text if necessary. $text = $variables['options']['html'] ? $variables['text'] : check_plain($variables['text']); - return '' . $text . ''; + $string = ('' . $text . ''); + $GLOBALS['safe_strings'][$string] = TRUE; + return $string; } /** diff --git a/core/includes/theme.inc b/core/includes/theme.inc index da3df24..4819ae9 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1122,7 +1122,8 @@ function theme($hook, $variables = array()) { // restore path_to_theme() $theme_path = $temp; - return $output; + $GLOBALS['safe_strings'][$output] = TRUE; + return ($output); } /** diff --git a/core/lib/Drupal/Component/Utility/String.php b/core/lib/Drupal/Component/Utility/String.php index 3cd6472..182518c 100644 --- a/core/lib/Drupal/Component/Utility/String.php +++ b/core/lib/Drupal/Component/Utility/String.php @@ -29,7 +29,9 @@ class String { * @ingroup sanitization */ public static function checkPlain($text) { - return htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); + $string = (htmlspecialchars($text, ENT_QUOTES, 'UTF-8')); + $GLOBALS['safe_strings'][$string] = TRUE; + return $string; } /** @@ -105,7 +107,9 @@ public static function format($string, array $args = array()) { // Pass-through. } } - return strtr($string, $args); + $output = strtr($string, $args); + $GLOBALS['safe_strings'][$output] = TRUE; + return $output; } /** diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php index 8d0d257..3600825 100644 --- a/core/lib/Drupal/Component/Utility/Xss.php +++ b/core/lib/Drupal/Component/Utility/Xss.php @@ -71,7 +71,7 @@ public static function filter($string, $allowed_tags = array('a', 'em', 'strong' // Named entities. $string = preg_replace('/&([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string); - return preg_replace_callback('% + $output = preg_replace_callback('% ( <(?=[^a-zA-Z!/]) # a lone < | # or @@ -81,6 +81,8 @@ public static function filter($string, $allowed_tags = array('a', 'em', 'strong' | # or > # just a > )%x', '\Drupal\Component\Utility\Xss::split', $string); + $GLOBALS['safe_strings'][$output] = TRUE; + return $output; } /** diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index a6cb957..36c7deb 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -118,9 +118,7 @@ public static function registerTwig(ContainerBuilder $container) { // files folder is writable. 'cache' => drupal_installation_attempted() ? FALSE : settings()->get('twig_cache', TRUE), 'base_template_class' => 'Drupal\Core\Template\TwigTemplate', - // @todo Remove in followup issue - // @see http://drupal.org/node/1712444. - 'autoescape' => FALSE, + 'autoescape' => TRUE, // @todo Remove in followup issue // @see http://drupal.org/node/1806538. 'strict_variables' => FALSE, diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php index 130ee9d..6826359 100644 --- a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php +++ b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php @@ -137,6 +137,7 @@ public function translate($string, array $args = array(), array $options = array $string = $translation === FALSE ? $string : $translation; if (empty($args)) { + // @todo: Deliberately not marked as safe. Or should it be? return $string; } else { diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 9cd17b0..c8aff5b 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -40,6 +40,8 @@ public function getFilters() { // @see TwigNodeTrans::compileString() 'passthrough' => new \Twig_Filter_Function('twig_raw_filter'), 'placeholder' => new \Twig_Filter_Function('twig_raw_filter'), + // Helper filter used to replace twig's original raw() filter. + 'twig_raw' => new \Twig_Filter_Function('twig_raw'), ); } diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php index 710faa0..e14a311 100644 --- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php +++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php @@ -58,6 +58,8 @@ function enterNode(\Twig_NodeInterface $node, \Twig_Environment $env) { * We use this to inject a call to render_var -> twig_render_var() * before anything is printed. * + * We also change the 'raw' filter to our own 'twig_raw' filter. + * * @see twig_render */ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) { @@ -70,6 +72,10 @@ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) { $node->getLine() ); } + else if ($node instanceof \Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) { + // Use our own twig_raw filter that returns Twig_Markup + $node->getNode('filter')->setAttribute('value', 'twig_raw'); + } if ($this->isReference) { if ($node instanceof \Twig_Node_Expression_Name) { diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 1a0d9d1..4ca3a13 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -711,7 +711,10 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE, if ($cache) { $cache_id = $format->format . ':' . $langcode . ':' . hash('sha256', $text); if ($cached = cache('filter')->get($cache_id)) { - return $cached->data; + // @todo: The caller is responsible that this is really safe. + $output = $cached->data; + $GLOBALS['safe_strings'][$output] = TRUE; + return $output; } } @@ -752,6 +755,8 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE, cache('filter')->set($cache_id, $text, CacheBackendInterface::CACHE_PERMANENT, array('filter_format' => $format->format)); } + // @todo: The caller is responsible that this is really safe. + $GLOBALS['safe_strings'][$text] = TRUE; return $text; } diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine index b541cdc..9891cd2 100644 --- a/core/themes/engines/twig/twig.engine +++ b/core/themes/engines/twig/twig.engine @@ -110,12 +110,20 @@ function twig_render_var($arg) { return NULL; } - // Keep Twig_Markup objects intact to prepare for later autoescaping support + // Keep Twig_Markup objects intact to support autoescaping. if ($arg instanceOf Twig_Markup) { return $arg; } + // For known objects with __toString() methods, return a Twig_Markup instance. + if ($arg instanceof \Drupal\Core\Template\RenderWrapper || $arg instanceof \Drupal\Core\Template\Attribute) { + return new Twig_Markup((string) $arg, 'UTF-8'); + } + if (is_scalar($arg)) { + if (isset($GLOBALS['safe_strings'][$arg])) { + return new Twig_Markup($arg, 'UTF-8'); + } return $arg; } @@ -127,7 +135,7 @@ function twig_render_var($arg) { } // This is a normal render array. - return render($arg); + return new Twig_Markup(render($arg), 'UTF-8'); } /** @@ -155,3 +163,20 @@ function twig_show($element) { } // @todo Add warning in else case } + +/** + * Replacement function for twig's raw filter + * + * This needs to be used, because the default raw + * filter gets optimized out. + * + * As we wrap everything that is printed in twig_render() + * we need to return Twig_Markup instead. + * This allows to hold the information that a value was + * marked 'raw' by the template author. + * + * @see TwigNodeVisitor + */ +function twig_raw($string) { + return new Twig_Markup($string, 'UTF-8'); +} diff --git a/core/vendor/twig/twig/lib/Twig/Extension/Core.php b/core/vendor/twig/twig/lib/Twig/Extension/Core.php index 26e7017..898eff4 100644 --- a/core/vendor/twig/twig/lib/Twig/Extension/Core.php +++ b/core/vendor/twig/twig/lib/Twig/Extension/Core.php @@ -847,6 +847,8 @@ function twig_in_filter($value, $compare) */ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) { + // @todo Create upstream pull request to remove unneeded is_object() call + // for speed. Would be nice to profile this as well. if ($autoescape && is_object($string) && $string instanceof Twig_Markup) { return $string; }