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;
}