diff -u b/core/includes/theme.inc b/core/includes/theme.inc --- b/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -16,6 +16,7 @@ use Drupal\Component\Utility\Xss; use Drupal\Core\Config\Config; use Drupal\Core\Config\StorageException; +use Drupal\Core\Render\SafeString; use Drupal\Core\Template\Attribute; use Drupal\Core\Theme\ThemeSettings; use Drupal\Component\Utility\NestedArray; @@ -1066,6 +1067,73 @@ } /** + * Prepares variables for inline list templates. + * + * Default template: inline-list.html.twig. + * + * @param array $variables + * An associative array containing: + * - items: An array of items to be displayed in the list. Each item can be + * either a string or a render array. If #type, #theme, or #markup + * properties are not specified for child render arrays, they will be + * inherited from the parent list, allowing callers to specify larger + * nested lists without having to explicitly specify and repeat the + * render properties for all nested child lists. + * - separator: A string to separate list items. + * + * @see https://www.drupal.org/node/1842756 + */ +function template_preprocess_inline_list(&$variables) { + foreach ($variables['items'] as &$item) { + $attributes = array(); + // If the item value is an array, then it is a render array. + if (is_array($item)) { + // List items support attributes via the '#wrapper_attributes' property. + if (isset($item['#wrapper_attributes'])) { + $attributes = $item['#wrapper_attributes']; + } + // Determine whether there are any child elements in the item that are not + // fully-specified render arrays. If there are any, then the child + // elements present nested lists and we automatically inherit the render + // array properties of the current list to them. + foreach (Element::children($item) as $key) { + $child = &$item[$key]; + // If this child element does not specify how it can be rendered, then + // we need to inherit the render properties of the current list. + if (!isset($child['#type']) && !isset($child['#theme']) && !isset($child['#markup'])) { + // Since inline-list.html.twig supports both strings and render arrays + // as items, the items of the nested list may have been specified as + // the child elements of the nested list, instead of #items. For + // convenience, we automatically move them into #items. + if (!isset($child['#items'])) { + // This is the same condition as in + // \Drupal\Core\Render\Element::children(), which cannot be used + // here, since it triggers an error on string values. + foreach ($child as $child_key => $child_value) { + if ($child_key[0] !== '#') { + $child['#items'][$child_key] = $child_value; + unset($child[$child_key]); + } + } + } + // Lastly, inherit the original theme variables of the current list. + $child['#theme'] = $variables['theme_hook_original']; + } + } + } + + // Set the item's value and attributes for the template. + $item = array( + 'value' => $item, + 'attributes' => new Attribute($attributes), + ); + } + + // Since the separator may be user-specified, it must be sanitized. + $variables['separator'] = SafeString::create(Xss::filterAdmin($variables['separator'])); +} + +/** * Returns HTML for an indentation div; used for drag and drop tables. * * @param $variables @@ -1726,8 +1794,8 @@ 'item_list' => array( 'variables' => array('items' => array(), 'title' => '', 'list_type' => 'ul', 'attributes' => array(), 'empty' => NULL, 'context' => array()), ), - 'item_list__inline' => array( - 'variables' => array('items' => array(), 'title' => '', 'list_type' => 'inline', 'attributes' => array(), 'empty' => NULL, 'context' => array()), + 'inline_list' => array( + 'variables' => array('items' => array(), 'separator' => ', ', 'attributes' => array(), 'empty' => NULL, 'context' => array()), ), 'feed_icon' => array( 'variables' => array('url' => NULL, 'title' => NULL), reverted: --- b/core/modules/system/system.module +++ a/core/modules/system/system.module @@ -256,14 +256,6 @@ /** * Implements hook_theme_suggestions_HOOK(). */ -function system_theme_suggestions_item_list(array $variables) { - $types = ['ul', 'ol', 'inline']; - return theme_get_suggestions($types, 'item_list'); -} - -/** - * Implements hook_theme_suggestions_HOOK(). - */ function system_theme_suggestions_page(array $variables) { if (\Drupal::service('path.matcher')->isFrontPage()) { $path_args = ['']; reverted: --- b/core/modules/system/templates/item-list--inline.html.twig +++ /dev/null @@ -1,33 +0,0 @@ -{# -/** - * @file - * Default theme implementation for an item list for comma separated inline. - * - * Available variables: - * - items: A list of items. Each item contains: - * - attributes: HTML attributes to be applied to each list item. - * - value: The content of the list element. - * - title: The title of the list. - * - list_type: The tag for list element ("ul" or "ol"). - * - attributes: HTML attributes to be applied to the list. - * - empty: A message to display when there are no items. Allowed value is a - * string or render array. - * - * @see template_preprocess_item_list() - * - * @ingroup themeable - */ -#} -{% if items or empty -%} - {%- if title is not empty -%} -

{{ title }}

- {%- endif -%} - - {%- if items -%} - {%- for item in items -%} - {{ item }}{{ loop.last ? '' : ', ' }} - {%- endfor -%} - {%- else -%} - {{- empty -}} - {%- endif -%} -{%- endif %} diff -u b/core/modules/views_ui/src/ViewListBuilder.php b/core/modules/views_ui/src/ViewListBuilder.php --- b/core/modules/views_ui/src/ViewListBuilder.php +++ b/core/modules/views_ui/src/ViewListBuilder.php @@ -109,7 +109,7 @@ 'tag' => $view->get('tag'), 'path' => array( 'data' => array( - '#theme' => 'item_list__inline', + '#theme' => 'inline_list', '#items' => $this->getDisplayPaths($view), ), ), only in patch2: unchanged: --- a/core/lib/Drupal/Core/Render/Element/Url.php +++ b/core/lib/Drupal/Core/Render/Element/Url.php @@ -25,7 +25,7 @@ * '#size' => 30, * ... * ); - * @end_code + * @endcode * * @see \Drupal\Core\Render\Element\Textfield * only in patch2: unchanged: --- a/core/modules/node/src/Plugin/Search/NodeSearch.php +++ b/core/modules/node/src/Plugin/Search/NodeSearch.php @@ -332,11 +332,9 @@ protected function prepareResults(StatementInterface $found) { unset($build['#theme']); $build['#pre_render'][] = array($this, 'removeSubmittedInfo'); - // Fetch comment count for snippet. - $rendered = SafeMarkup::set( - $this->renderer->renderPlain($build) . ' ' . - SafeMarkup::escape($this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode))) - ); + // Fetch comments for snippet. + $rendered = $this->renderer->renderPlain($build); + $rendered .= ' ' . $this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode)); $extra = $this->moduleHandler->invokeAll('node_search_result', array($node, $item->langcode)); only in patch2: unchanged: --- a/core/modules/search/search.module +++ b/core/modules/search/search.module @@ -618,7 +618,8 @@ function search_mark_for_reindex($type = NULL, $sid = NULL, $langcode = NULL) { /** * Returns snippets from a piece of text, with search keywords highlighted. * - * Used for formatting search results. + * Used for formatting search results. All HTML tags will be stripped from + * $text. * * @param string $keys * A string containing a search query. only in patch2: unchanged: --- a/core/modules/search/src/Tests/SearchCommentTest.php +++ b/core/modules/search/src/Tests/SearchCommentTest.php @@ -126,6 +126,23 @@ function testSearchResultsComment() { $edit_comment['comment_body[0][format]'] = $full_html_format_id; $this->drupalPostForm('comment/reply/node/' . $node->id() .'/comment', $edit_comment, t('Save')); + // Post a comment with an evil script tag in the comment subject and a + // script tag nearby a keyword in the comment body. Use the 'FULL HTML' text + // format so the script tag stored. + $edit_comment2 = array(); + $edit_comment2['subject[0][value]'] = ""; + $edit_comment2['comment_body[0][value]'] = "nearbykeyword"; + $edit_comment2['comment_body[0][format]'] = $full_html_format_id; + $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment2, t('Save')); + + // Post a comment with a keyword inside an evil script tag in the comment + // body. Use the 'FULL HTML' text format so the script tag is stored. + $edit_comment3 = array(); + $edit_comment3['subject[0][value]'] = 'asubject'; + $edit_comment3['comment_body[0][value]'] = ""; + $edit_comment3['comment_body[0][format]'] = $full_html_format_id; + $this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit_comment3, t('Save')); + // Invoke search index update. $this->drupalLogout(); $this->cronRun(); @@ -152,6 +169,39 @@ function testSearchResultsComment() { $this->assertNoRaw(t('n/a'), 'HTML in comment body is not hidden.'); $this->assertNoEscaped($edit_comment['comment_body[0][value]'], 'HTML in comment body is not escaped.'); + // Search for the evil script comment subject. + $edit = array( + 'keys' => 'subjectkeyword', + ); + $this->drupalPostForm('search/node', $edit, t('Search')); + + // Verify the evil comment subject is escaped in search results. + $this->assertRaw('<script>alert('subjectkeyword');'); + $this->assertNoRaw('