diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php
index 172a323..ee540cc 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -282,4 +282,51 @@ public static function placeholder($text) {
return $string;
}
+ /**
+ * Replaces all occurrences of the search string with the replacement string.
+ *
+ * Functions identically to str_replace(), but marks the returned output as
+ * safe if all the inputs and the subject have also been marked as safe.
+ *
+ * @param string|array $search
+ * The value being searched for. An array may be used to designate multiple
+ * values to search for.
+ * @param string|array $replace
+ * The replacement value that replaces found search values. An array may be
+ * used to designate multiple replacements.
+ * @param string $subject
+ * The string or array being searched and replaced on.
+ *
+ * @return string
+ * The passed subject with replaced values.
+ */
+ public static function replace($search, $replace, $subject) {
+ $output = str_replace($search, $replace, $subject);
+
+ // If any replacement is unsafe, then the output is also unsafe, so just
+ // return the output.
+ if (!is_array($replace)) {
+ if (!SafeMarkup::isSafe($replace)) {
+ return $output;
+ }
+ }
+ else {
+ foreach ($replace as $replacement) {
+ if (!SafeMarkup::isSafe($replacement)) {
+ return $output;
+ }
+ }
+ }
+
+ if (!SafeMarkup::isSafe($subject)) {
+ return $output;
+ }
+ else {
+ // If we have reached this point, then all replacements were safe, and
+ // therefore if the subject was also safe, then the entire output is also
+ // safe, and should be marked as such.
+ return SafeMarkup::set($output);
+ }
+ }
+
}
diff --git a/core/lib/Drupal/Core/Render/Element/HtmlTag.php b/core/lib/Drupal/Core/Render/Element/HtmlTag.php
index 553767f..7d5c934 100644
--- a/core/lib/Drupal/Core/Render/Element/HtmlTag.php
+++ b/core/lib/Drupal/Core/Render/Element/HtmlTag.php
@@ -46,7 +46,12 @@ public function getInfo() {
* Pre-render callback: Renders a generic HTML tag with attributes into #markup.
*
* Note: It is the caller's responsibility to sanitize any input parameters.
- * This callback does not perform sanitization.
+ * This callback does not perform sanitization. Despite the result of this
+ * pre-render callback being a #markup element, it is not passed through
+ * \Drupal\Component\Utility\Xss::filterAdmin. This is because it is marked
+ * safe here, which causes \Drupal\Component\Utility\SafeMarkup::checkAdminXss
+ * to regard it as safe and bypass the call to
+ * \Drupal\Component\Utility\Xss::filterAdmin,
*
* @param array $element
* An associative array containing:
@@ -94,7 +99,7 @@ public static function preRenderHtmlTag($element) {
$markup = SafeMarkup::set($markup);
}
if (!empty($element['#noscript'])) {
- $element['#markup'] = '';
+ $element['#markup'] = SafeMarkup::format('', ['!markup' => $markup]);
}
else {
$element['#markup'] = $markup;
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index 9ba7613..0bb6ba9 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -253,9 +253,8 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
$elements['#children'] = '';
}
- // @todo Simplify after https://www.drupal.org/node/2273925.
if (isset($elements['#markup'])) {
- $elements['#markup'] = SafeMarkup::set($elements['#markup']);
+ $elements['#markup'] = SafeMarkup::checkAdminXss($elements['#markup']);
}
// Assume that if #theme is set it represents an implemented hook.
@@ -553,7 +552,10 @@ public function generateCachePlaceholder($callback, array &$context) {
'token' => Crypt::randomBytesBase64(55),
];
- return '
' . $this->randomMachineName() . '
'; + $footer_string = '' . $this->randomMachineName() . '
'; + $empty_string = '' . $this->randomMachineName() . '
'; $view->header['test_example']->options['string'] = $header_string; $view->header['test_example']->options['empty'] = TRUE; @@ -104,12 +105,13 @@ public function testRenderArea() { $view->empty['test_example']->options['string'] = $empty_string; - // Check whether the strings exists in the output. + // Check whether the strings exists in the output and they are being + // sanitized. $output = $view->preview(); $output = drupal_render($output); - $this->assertTrue(strpos($output, $header_string) !== FALSE); - $this->assertTrue(strpos($output, $footer_string) !== FALSE); - $this->assertTrue(strpos($output, $empty_string) !== FALSE); + $this->assertTrue(strpos($output, Xss::filterAdmin($header_string)) !== FALSE, 'Views header exists in the output and is sanitized'); + $this->assertTrue(strpos($output, Xss::filterAdmin($footer_string)) !== FALSE, 'Views footer exists in the output and is sanitized'); + $this->assertTrue(strpos($output, Xss::filterAdmin($empty_string)) !== FALSE, 'Views empty exists in the output and is sanitized'); } /** diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 39589c2..a0ca58b 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -683,7 +683,7 @@ function views_pre_render_views_form_views_form($element) { } // Apply substitutions to the rendered output. - $element['output'] = array('#markup' => str_replace($search, $replace, drupal_render($element['output']))); + $element['output'] = array('#markup' => SafeMarkup::replace($search, $replace, drupal_render($element['output']))); // Sort, render and add remaining form fields. $children = Element::children($element, TRUE); diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc index f2f2b67..4aafde4 100644 --- a/core/modules/views_ui/views_ui.theme.inc +++ b/core/modules/views_ui/views_ui.theme.inc @@ -147,21 +147,35 @@ function theme_views_ui_build_group_filter_form($variables) { foreach (Element::children($form['group_items']) as $group_id) { $form['group_items'][$group_id]['value']['#title'] = ''; + $default = array( + $form['default_group'][$group_id], + $form['default_group_multiple'][$group_id], + ); + $link = [ + '#type' => 'link', + '#url' => Url::fromRoute('', '#suffix' => '', ]; - $expected_output = '
'; + $expected_output = '' . $context['bar'] . '
' . $context['bar'] . '
';
// #cache disabled.
$element = $test_element;
@@ -530,7 +530,7 @@ public function testChildElementPlaceholder() {
'#suffix' => ''
],
];
- $expected_output = '' . "\n"; + $expected_output = '' . $context['bar'] . '
' . $context['bar'] . '
' . "\n";
// #cache disabled.
$element = $test_element;
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
index 78fca24..2e6a33d 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
@@ -81,6 +81,10 @@ public function providerTestRenderBasic() {
$data[] = [[
'child' => ['#markup' => 'bar'],
], 'bar'];
+ // XSS filtering test.
+ $data[] = [[
+ 'child' => ['#markup' => "This is test"],
+ ], "This is alert('XSS') test"];
// #children set but empty, and renderable children.
$data[] = [[
'#children' => '',
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
index 0a8a28a..1ce0bf0 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
@@ -255,7 +255,7 @@ public static function callback(array $element, array $context) {
public static function placeholder(array $element, array $context) {
$placeholder = \Drupal::service('renderer')->generateCachePlaceholder(__NAMESPACE__ . '\\PostRenderCache::placeholder', $context);
$replace_element = array(
- '#markup' => '' . $context['bar'] . '
',
'#attached' => array(
'drupalSettings' => [
'common_test' => $context,