Problem/Motivation

Follow-up to #2527360: Review all usages of Xss::filter(), Xss::filterAdmin(), and Html::escape()

The safe string list in SafeMarkup is difficult to control and has potential side effects. We should not use it if we don't have to.

Proposed resolution

Add two new render array keys #markup_xss_tags and #markup_safe_strategy. #markup_xss_tags can take an array of tags to change how #markup is filtered. In HEAD #markup is filtered using the admin tag list. #markup_safe_strategy allows us to switch from a xss filtering strategy to an escaping strategy using htmlspecialchars().

This patch also changes #title to be able to be a render array since different implementations have different strategies (it's a bit of mess). Other functions, for example, search_excerpt() are changed to return a render array instead of a safe marked string to allow for late filtering/escaping by the render system.

Remaining tasks

  • Agree approach
  • Write and review patch

User interface changes

None

API changes

  • #title can be a render array
  • search_excerpt() returns a render array
  • SafeMarkup::xssFilter() is removed
  • aggregator_filter_xss() is removed
  • aggregator_xss_tags() is added
  • Drupal\aggregator\Controller\AggregatorController::feedTitle() returns a render array
  • Drupal\menu_ui\Controller\MenuController returns a render array
  • Drupal\taxonomy\Controller::vocabularyTitle returns a render array
  • Drupal\taxonomy\Controller::termTitle returns a render array

Data model changes

None

CommentFileSizeAuthor
#41 2549791-2.40.patch54.65 KBalexpott
#41 35-40-interdiff.txt4.94 KBalexpott
#36 2549791-2.35.patch53.23 KBalexpott
#36 31-35-interdiff.txt5.38 KBalexpott
#31 interdiff.txt15.51 KBWim Leers
#31 2549791.31.patch51.91 KBWim Leers
#30 interdiff.txt18.23 KBWim Leers
#30 2549791.30.patch52.14 KBWim Leers
#20 2549791.20.patch46.23 KBalexpott
#20 18-20-interdiff.txt2.46 KBalexpott
#18 2549791.18.patch46.14 KBalexpott
#18 15-18-interdiff.txt836 bytesalexpott
#15 2549791.15.patch45.94 KBalexpott
#15 13-15-interdiff.txt3.96 KBalexpott
#13 2549791.12.patch45.92 KBalexpott
#13 10-12-interdiff.txt13.37 KBalexpott
#10 8-10-interdiff.txt17.53 KBalexpott
#10 2549791.10.patch46.55 KBalexpott
#8 2549791.8.patch81.99 KBalexpott
#8 5-8-interdiff.txt3.96 KBalexpott
#5 2549791.5.patch81.13 KBalexpott
#5 4-5-interdiff.txt5.92 KBalexpott
#4 2549791.4.do-not-test.patch28.29 KBalexpott
#4 2549791.4-with-2501931.patch65.31 KBalexpott
#4 2-4-interdiff.txt4.08 KBalexpott
#2 2549791.2.patch28.1 KBalexpott
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

alexpott created an issue. See original summary.

alexpott’s picture

Status: Active » Needs review
FileSize
28.1 KB

Status: Needs review » Needs work

The last submitted patch, 2: 2549791.2.patch, failed testing.

alexpott’s picture

Status: Needs work » Needs review
FileSize
4.08 KB
65.31 KB
28.29 KB

So we really need #2501931: Remove SafeMarkup::set in twig_render_template() and ThemeManager and FieldPluginBase:advancedRender in since aggregator is relying on SafeMarkup set side effects to traverse the views render pipeline without side effects. Patch attached includes the latest path from that issue.

Also I think we should fix search_excerpt to not do the early render - just need to fix the test. And potentially add a template for head title and put all the logic from html process in there so that we don't have to do the early render.

alexpott’s picture

Improved documentation in the patch and the issue summary.

Status: Needs review » Needs work

The last submitted patch, 5: 2549791.5.patch, failed testing.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Component/Utility/Xss.php
    @@ -23,19 +23,13 @@ class Xss {
     
    +  protected static $htmlTags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd');
    

    I can haz documentation?

  2. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -737,6 +737,36 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +    if (SafeMarkup::isSafe($elements['#markup'])) {
    +      // Nothing to do as #markup is already marked as safe.
    +      $string = $elements['#markup'];
    +    }
    ...
    +    // @todo Do we need to clean up #markup_safe_strategy and #markup_xss_tags?
    +    return SafeString::create($string);
    

    Would it make sense to do an early return given that we already know its safeness?

  3. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -737,6 +737,36 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +    elseif ($strategy == $this::SAFE_MARKUP_ESCAPE) {
    

    opinions on $this vs. static:: ?

  4. +++ b/core/modules/aggregator/src/ItemViewBuilder.php
    @@ -26,7 +26,8 @@ public function buildComponents(array &$build, array $entities, array $displays,
    +          '#markup' => $entity->getDescription(),
    +          '#markup_xss_tags' => aggregator_xss_tags(),
    

    I'm not entirely sure whether making the markup_safe_strategy implicit is the right thing to do here

  5. +++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
    @@ -37,6 +37,7 @@ public function process($text, $langcode) {
    +      $renderer = \Drupal::service('renderer');
    

    Do you mind putting an inline doc about the type here?

alexpott’s picture

Status: Needs work » Needs review
FileSize
3.96 KB
81.99 KB

Re #7 - thanks @dawehner

  1. You canz
  2. Yes it would
  3. We prefer static:: (oops)
  4. We have to default to Xss admin filtering since this would be a massive BC break and even more painful.
  5. No at all
Wim Leers’s picture

I have some doubts/concerns about this patch.

  1. +++ b/core/lib/Drupal/Component/Utility/Xss.php
    @@ -347,4 +343,13 @@ public static function getAdminTagList() {
    +   * Gets the standard list of html tags allowed by Xss::filter().
    ...
    +   *   The list of html tags allowed by Xss::filter().
    

    s/html/HTML/

  2. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -737,6 +737,35 @@ public function addCacheableDependency(array &$elements, $dependency) {
       /**
    +   * Escapes or filters #markup as required.
    +   *
    +   * @param array $elements
    +   *   A render array with #markup set.
    +   *
    +   * @return \Drupal\Component\Utility\SafeStringInterface|string
    +   *   The escaped string wrapped in a SafeString object. If
    +   *   SafeMarkup::isSafe($elements['#markup']) returns TRUE, it won't be
    +   *   escaped again.
    +   */
    

    This is not yet documenting, not even mentioning #markup_safe_strategy and the possible values for that.

  3. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -737,6 +737,35 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +  protected function safeMarkup(array $elements) {
    

    "safeMarkup" is a strange method name IMO, because it doesn't have a verb.

  4. +++ b/core/lib/Drupal/Core/Render/RendererInterface.php
    @@ -13,6 +13,22 @@
       /**
    +   * #markup_safe_strategy indicating #markup should be filtered.
    +   *
    +   * @see \Drupal\Core\Render\Renderer::safeMarkup()
    +   * @see \Drupal\Component\Utility\Xss::filter()
    +   */
    +  const SAFE_MARKUP_XSS = 'xss';
    +
    +  /**
    +   * #markup_safe_strategy indicating #markup should be escaped.
    +   *
    +   * @see \Drupal\Core\Render\Renderer::safeMarkup()
    +   * @see \Drupal\Component\Utility\Html::encodeEntities()
    +   */
    +  const SAFE_MARKUP_ESCAPE = 'escape';
    

    It very much concerns me that we're now adding even escaping/filtering logic to Renderer.

    Isn't Renderer more complex than we'd like already?

    Is there really no other way?

  5. +++ b/core/modules/aggregator/src/ItemViewBuilder.php
    @@ -26,7 +26,8 @@ public function buildComponents(array &$build, array $entities, array $displays,
    -          '#markup' => aggregator_filter_xss($entity->getDescription()),
    +          '#markup' => $entity->getDescription(),
    +          '#markup_xss_tags' => aggregator_xss_tags(),
    
    +++ b/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorXSSFormatter.php
    @@ -36,7 +36,8 @@ public function viewElements(FieldItemListInterface $items) {
    -        '#markup' => aggregator_filter_xss($item->value),
    +        '#markup' => $item->value,
    +        '#markup_xss_tags' => aggregator_xss_tags(),
    

    I think these could be converted to #type => 'processed_text', and use a text format that uses filter_html to only allow the specified tags.

    That's really what the aggregator is doing here: consuming content from elsewhere (the content creator is not a person, but an external site), whose output needs to be filtered through the filter system, just like for any node that any content creator creates.

  6. +++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
    @@ -45,8 +47,8 @@ public function process($text, $langcode) {
    -        $caption = SafeMarkup::xssFilter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br'));
    -
    +        $caption = ['#markup' => $caption, '#markup_xss_tags' => ['a', 'em', 'strong', 'cite', 'code', 'br']];
    +        $caption = $renderer->render($caption);
    

    It seems wrong to use the render system to do filtering in a filter.

  7. +++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
    @@ -64,7 +66,7 @@ public function process($text, $langcode) {
    -        $altered_html = drupal_render($filter_caption);
    +        $altered_html = $renderer->render($filter_caption);
    

    (This uses the render system to render some markup. Very different to the above.)

alexpott’s picture

Re #9

  1. Fixed all instances of html in Xss to be HTML
  2. Improved documentation
  3. Changed to ensureMarkupIsSafe at @Wim Leers recommendation
  4. We've already got escaping and filtering logic in renderer - we have to do it somewhere and as the renderer is output for twig it has to take care of safeness. I've added documentation to explain the relationship to auto-escape.
  5. Feels like followup material to me - will open an issue.
  6. Yeah this should use FilteredString when we have it.
  7. Yep

Changed markup_xss_tags to markup_allowed_tags as @Wim Leers advice on IRC plus added more docs (again with the help of Wim)

Wim Leers’s picture

Interdiff review:

  1. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -739,6 +739,18 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +   * automatically escapes any html that is not known to be safe. Due to this
    

    s/html/HTML/

  2. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -739,6 +739,18 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +   * the renderer system needs to ensure that all markup it generates is marked
    

    "renderer system" should be either "Renderer" or "render system" :)

  3. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -739,6 +739,18 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +   * By default all #markup is filtered for XSS using the admin tag list. Render
    

    "filtered for XSS using" -> "filtered for XSS protection using" or maybe "filtered to protect against XSS using"

  4. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -739,6 +739,18 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +   * arrays can alter the list of tags to filter using the #markup_allowed_tags
    

    s/tags to filter/tags allowed by the filtering/

    ?

  5. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -739,6 +739,18 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +   * key. This value should be an array of tags that Xss::filter() would accept.
    ...
    +   * #markup_safe_strategy key to RendererInterface::SAFE_MARKUP_ESCAPE. If the
    

    s/key/property/

  6. +++ b/core/lib/Drupal/Core/Render/theme.api.php
    @@ -273,21 +273,21 @@
    + *   which tags are using to filter the string. The value should be an array of
    

    s/tags are using/tags are allowed when filtering/

    s/string/markup/ ?


Based on the above changes/discussions, more remarks on other parts of the patch:

  1. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -737,6 +737,51 @@ public function addCacheableDependency(array &$elements, $dependency) {
    +   * @see \Drupal\Core\Render\RendererInterface::SAFE_MARKUP_ESCAPE
    

    Why no @see to the other constant?

  2. +++ b/core/lib/Drupal/Core/Render/RendererInterface.php
    @@ -13,6 +13,22 @@
    +  const SAFE_MARKUP_XSS = 'xss';
    ...
    +  const SAFE_MARKUP_ESCAPE = 'escape';
    

    I think MARKUP_SAFE_STRATEGY_something would be clearer, because it'd closely match #markup-safe_strategy.

  3. +++ b/core/lib/Drupal/Core/Render/theme.api.php
    @@ -271,10 +271,24 @@
    + *   @see \Drupal\Core\Render\RendererInterface::SAFE_MARKUP_ESCAPE
    

    Again only an @see to one of the two constants.

  4. +++ b/core/modules/aggregator/aggregator.module
    @@ -157,16 +157,13 @@ function aggregator_cron() {
    + * Gets the permitted list of tags.
    ...
    + *   The array of permitted tags.
    

    s/permitted/allowed/

  5. +++ b/core/modules/aggregator/aggregator.module
    @@ -157,16 +157,13 @@ function aggregator_cron() {
    +function aggregator_xss_tags() {
    

    s/xss/allowed/

alexpott’s picture

Addressed all the points of the review... went for MARKUP_SAFE_STRATEGY_FILTER and MARKUP_SAFE_STRATEGY_ESCAPE. Thanks @Wim Leers.

I think that SafeMarkup::xssFilter() is okay to remove because it was only introduced in #2506195: Remove SafeMarkup::set() from Xss::filter() and therefore has only existed for 1 month.

alexpott’s picture

Here's the patch :) - Also updated the CR

Wim Leers’s picture

I have no more remarks, besides one silly nit below. But I think this needs additional review by somebody else before it can be RTBC'd.

+++ b/core/modules/aggregator/aggregator.module
@@ -157,12 +157,12 @@ function aggregator_cron() {
-function aggregator_xss_tags() {
+function aggregator_allowed_tags() {

Should we prefix this with an underscore, to indicate it's not a public API?

Or at least add @internal?

alexpott’s picture

@Wim Leers I think marking it with @internal and an underscore makes sense.

xjm’s picture

@Wim Leers pinged me this issue. I think removing this method from SafeMarkup is an excellent idea, and it probably makes sense for render arrays to be able to control the escaping strategy of #markup.

I'm somewhat nervous about adding even more magic keys to the Render ArrayPI, but it helps to conceptualize a render array as an object-in-waiting.

I'm also nervous about anything that requires us to add even more renderPlain() calls (basically just using render arrays as "renderable markup objects" locally when the context doesn't support them).

I pinged @catch and @Fabianx for their feedback too.

alexpott’s picture

This patch only adds one renderPlain call - and that is to a test.

alexpott’s picture

Using Html::escape().

Wim Leers’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -737,6 +738,54 @@ public function addCacheableDependency(array &$elements, $dependency) {
+   * @see htmlspecialchars()

+++ b/core/lib/Drupal/Core/Render/RendererInterface.php
@@ -13,6 +13,22 @@
+   * @see htmlspecialchars()

Shouldn't these now also point to Html::escape()?

alexpott’s picture

Status: Needs work » Needs review
FileSize
2.46 KB
46.23 KB

Good point @Wim Leers. Fixed.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Render/RendererInterface.php
@@ -13,6 +13,22 @@
+  const MARKUP_SAFE_STRATEGY_FILTER = 'xss';
...
+  const MARKUP_SAFE_STRATEGY_ESCAPE = 'escape';

I'm wondering whether the constants should reflect that we deal with HTML for those cases ...

catch’s picture

Discussed this in irc with alexpott and Wim Leers.

So the main concern I have with this is that search excepts, aggregator, contact messages are all extremely special cases - similar to Wim's point in #9. #markup isn't a special case, but we're adding a capability to it to support things that are.

However page titles, while also a special case aren't exactly esoteric.

So it really comes down to opening follow-ups to look at the places that require this, and see if we can do them differently (like using the filter system for aggregator and possibly search excerpts). Then maybe we can deprecate this in a minor release and/or remove in 9.x.

We also talked about adding a new #type, but that would then conflict with #markup.

In terms of the patch itself, I think it's fine.

alexpott’s picture

Thanks @catch and @Wim Leers. Opened the followups:

Re #21 in the discussion with @Wim Leers and @catch, @Wim Leers said:

Right, ok. So I think that it's great that you're concerned/want to be careful of "enforcing" considering safeness onto the Renderer. But, given 1) pragmatism, 2) reality being that we're so close to release, 3) reality being that Renderer/render arrays have evolved/grown more complex over YEARS and they've always been HTML-specific, I think it's okay for the Renderer to always handle safeness the way it now does/the patch does, because supporting other output formats is not going to be realistic anyway.

The renderer is about HTML at the moment. If we change that we need to do more work that just these constants.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs architectural review

Given #22 and #23, I think this is RTBC again.now RTBC!

14:40:34 <WimLeers> catch: do you still need to do an architectural review of https://www.drupal.org/node/2549791?
14:40:37 <Druplicon> https://www.drupal.org/node/2549791 => Remove SafeMarkup::xssFilter() and provide ability to define #markup escaping strategy and what tags to filter #2549791: Remove SafeMarkup::xssFilter() and provide ability to define #markup escaping strategy and what tags to filter => 23 comments, 11 IRC mentions
14:40:43 <WimLeers> (i.e. the issue we discussed this morning)
14:42:00 <catch> WimLeers: I could look again, but I feel like it's OK.
14:42:02 <WimLeers> ok
14:42:06 <WimLeers> catch: then I'm RTBC'ing.
joelpittet’s picture

Just throwing in a little request: Any chance the key could just be #allowed_tags instead of #markup_allowed_tags? or is the idea to discourage it's use? then I'm fine with it as is.

alexpott’s picture

@joelpittet key name is because it only affects #markup it does not affect all the other things we admin filter by default. Of course, if there are better key names let's make the change.

joelpittet’s picture

We don't do that with other types so I don't think it's a good to start that pattern here if that is the reason.

eg. #items isn't #item_list_items because it only works with #type => item_list.

Wim Leers’s picture

#27: the difference is that #items is specific to #type => 'item_list', i.e. you have to already specify that specific #type. But #markup is special: it's something that Renderer itself directly supports. If you don't specify a #type, you can still use #markup. Therefore I think it's okay to be more explicit and different compared to the common case.

catch’s picture

Status: Reviewed & tested by the community » Needs review

So I was about to commit this, but then I read Wim's comment and thought of #2012818: Remove #type 'markup'.

We also talked about adding a new #type, but that would then conflict with #markup.

A new #type would conflict with #markup, but if we moved #markup back to an element, then the new key support and accompanying logic can live in the render element. Also the original motivation for removing the element was because there was no distinction at all except more typing, but given the extra logic around #markup there's something to use there now. Not 100% sure it's better than the patch, but Wim Leers said in irc he'll try it.

Wim Leers’s picture

And done, as described in #29. This also moves and renames the constants from RendererInterface::MARKUP_SAFE_STRATEGY_something to Markup::SAFE_STRATEGY_something.

And because of this, I think #27 now does make sense. Doing that next.

Wim Leers’s picture

  • #markup_allowed_tags#allowed_tags
  • #markup_safe_strategy#safe_strategy
Wim Leers’s picture

This would effectively revert the CR at https://www.drupal.org/node/2036237.

dawehner’s picture

  1. +++ b/core/modules/search/search.module
    @@ -725,7 +725,7 @@ function search_excerpt($keys, $text, $langcode = NULL) {
    -      '#markup_safe_strategy' => RendererInterface::MARKUP_SAFE_STRATEGY_ESCAPE,
    +      '#markup_safe_strategy' => Markup::SAFE_STRATEGY_ESCAPE,
    

    Nice!

  2. +++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
    @@ -112,6 +113,25 @@ protected function setUp() {
    +      ->will($this->returnCallback(function ($type) {
    

    You can use ->willReturnCallback if you like

Wim Leers’s picture

2. I had that at one point, but lost it in my many attempts to minimize the changes in this area. Let's see if there are other nitpicks Alex or I can fix in one go :)

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Yeah its still RTBC

alexpott’s picture

Just noticed some unused use statements :) also took care of #33.2

Wim Leers’s picture

RTBC++

(I just moved code.)

catch’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Render/Element/Markup.php
@@ -0,0 +1,108 @@
+ *

One issue - these docs are for a completely different class.

Should we just say "Provides a render element for HTML as a string, with sanitization." or something?

alexpott’s picture

Assigned: Unassigned » alexpott

On it.

Wim Leers’s picture

Ugh, sorry, I forgot to update that :(

alexpott’s picture

Status: Needs work » Needs review
FileSize
4.94 KB
54.65 KB

Improved docs.

alexpott’s picture

Assigned: alexpott » Unassigned
Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/core/lib/Drupal/Core/Render/Element/Markup.php
@@ -13,12 +13,51 @@
+ * Usage example:
+ * @code
+ * $output['admin_filtered_string'] = array(
+ *   '#type' => 'markup',
+ *   '#markup' => '<em>This is filtered using the admin tag list</em>',
+ * );
+ * $output['filtered_string'] = array(
+ *   '#type' => 'markup',
+ *   '#markup' => '<em>This is filtered</em>',
+ *   '#allowed_tags' => ['strong'],
+ * );
+ * $output['escaped_string'] = array(
+ *   '#type' => 'markup',
+ *   '#markup' => '<em>This is escaped</em>',
+ *   '#safe_strategy' => Markup::SAFE_STRATEGY_ESCAPE,
+ * );
+ * @endcode

Excellent! :)

EDIT: dreditor WTF.

alexpott’s picture

RTBC++

(I just moved documentation.)

#37 ;)

catch’s picture

Status: Reviewed & tested by the community » Fixed

Thanks for the docs update.

Committed/pushed to 8.0.x, thanks!

  • catch committed d8a3d5d on 8.0.x
    Issue #2549791 by alexpott, Wim Leers: Remove SafeMarkup::xssFilter()...
Wim Leers’s picture

cilefen’s picture

In the CR, is it #markup_allowed_tags or #allowed_tags? It seems to contradict itself.

David_Rothstein’s picture

Status: Fixed » Active

I fixed the change notice now so it uses #allowed_tags consistently.

However the change notice does a good job explaining how to do this with #markup but does not really explain much about how to replace SafeMarkup::xssFilter() elsewhere in a render array.

For example if I have this:

  $render_array = array(
    '#theme' => 'something',
    '#description' => $description,
  );

and if the #theme callback is generic enough that it does not impose restrictions on what can be passed in (e.g. plain text vs HTML) then before this issue the calling code could just call SafeMarkup::xssFilter($description) to have it be treated as HTML.

Now with that no longer an option, I'm not sure what you're supposed to do. This might work:

  $render_array = array(
    '#theme' => 'something',
    '#description' => array(
      '#markup' => $description,
    ),
  );

although the nested properties look a little strange.

However, if you do that, doesn't that mean the theme preprocess function (and anything else that alters or interacts with the render array along the way) now needs to consider the possibility that #description could be either a string or an array?

Or is the idea that it should always be an array, and you're not supposed to use strings in render API properties at all anymore?

I'm confused about how all this works and I think more info is needed in the change notice.

jhodgdon’s picture

Status: Active » Needs work
Issue tags: +Needs change record

This patch that was committed in modules/search changed the return value of the search_excerpt() function from a string to a render array.

This is actually a fairly large API change. There is no change notice. There are contrib modules that use this function, such as Display Suite, so it absolutely needs a change notice. Please write one. The patch may also have changed other API functions, I haven't looked, but this change (a) should have been at least passed by the Search module maintainers and (b) doesn't have a change notice.

jhodgdon’s picture

I took a look through the patch. aggregator_filter_xss() is apparently removed, and I cannot see a change notice for this either.

alexpott’s picture

Status: Needs work » Needs review

re #49 - the render array still supports string properties. These generally will be auto escaped by Twig and this is not changed in this patch. If you want them to include markup then either you'll be using SafeMarkup::format(), t() or one of the "special" render properties i.e. #markup, #prefix or #suffix.

re #50/#51 Created https://www.drupal.org/node/2564261 and https://www.drupal.org/node/2564263

David_Rothstein’s picture

@alexpott, I don't see how those would work with my example in #49, where $description comes from an unknown source (e.g. user input) and is expected to contain HTML.

I guess it's technically possible to do something like this:

  $render_array = array(
    '#theme' => 'something',
    '#description' => SafeMarkup::format('@description', array(
      '@description' => Xss::filterAdmin($description),
    )),
  );

and it would work... but I can't imagine this is what we want to recommend.

alexpott’s picture

@David_Rothstein I think that if the description contains markup we should be using a render array. For the FieldApi is uses a special FieldFilterString object to do this - which whilst not ideal - is one way of going. In the example you give the $description would be escaped because Xss::filterAdmin() does not put it in the safe list.

David_Rothstein’s picture

In the example you give the $description would be escaped because Xss::filterAdmin() does not put it in the safe list.

My example has SafeMarkup::format() which would put it in the safe list.

I think that if the description contains markup we should be using a render array.

So you're basically suggesting what I had in #49, then?:

  $render_array = array(
    '#theme' => 'something',
    '#description' => array(
      '#markup' => $description,
    ),
  );

But if so, see the questions I had there. Basically we are saying that preprocess functions/etc are going to need to be very aware of this. If they just take a variable and treat it as a string, then the calling code can't really decide to send in HTML (at least not using the recommended, non-contortionist method).

alexpott’s picture

@David_Rothstein the @placeholder would cause it to be escaped. And yes preprocess functions should be aware of this. Because as you can see from the patch we didn't ever have to actually do this.

David_Rothstein’s picture

Oh, yikes - I meant !description but in fact that doesn't treat it as safe either now. Anyway, we all agree it wasn't a good workaround anyway :)

We at least need something in the change notice about this since it's a common use case, but I'm not quite sure what to recommend. I think it basically means that if you're defining a theme implementation that you expect other modules to call, you need to consider that the calling code might pass in an array rather than a string and handle both cases (or require/document that they only pass in an array). Or decide on your level that this should never have HTML, in which case you can still decide to treat it as a string only.... Still a little confused how to do this in practice.

Fabianx’s picture

There are 2 unpublished change record drafts on this issue.

Are they correct?

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Fabianx’s picture

Status: Closed (fixed) » Fixed

There are still 2 unpublished change record drafts on this issue.

Are they correct?

alexpott’s picture

They are correct - fwiw I've published them.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.