Proposed commit message

Issue #2469563 by marthinal, pjonckiere, b0unty, laurii, dawehner, Fabianx: Double-escape on Views filters

Problem/Motivation

When Views filter list has groups, some of the links on the filters list gets double-escaped.
See #2297711: Fix HTML escaping due to Twig autoescape and https://www.drupal.org/node/2311123 for backstory.

Proposed resolution

  • Use SafeMarkup::format() instead of concatenating directly.

-

Remaining tasks

-Create a patch fixing the bug

User interface changes

-

API changes

-

Comments

marthinal’s picture

Priority: Normal » Major
Status: Active » Needs review
StatusFileSize
new28.37 KB
new727 bytes

Probably Major.

We need to mark as secure and probably when creating the link.

lauriii’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

The change looks good to me. All the text that is being marked as safe in the patch can be considered as safe. We could still add a test coverage for this.

marthinal’s picture

Issue tags: +drupaldevdays
marthinal’s picture

Assigned: Unassigned » marthinal
marthinal’s picture

Status: Needs work » Needs review
StatusFileSize
new1.49 KB

Using inline_template to avoid SafeMarkup::set().

lauriii’s picture

Status: Needs review » Needs work

Good work, I like the usage of inline template here if it solves the problem!

+++ b/core/themes/engines/twig/twig.engine
@@ -158,7 +158,7 @@ function twig_drupal_join_filter($value, $glue = '') {
+    $output .= $separator . render($item);

Is this change intentional? I don't think this fits into the scope of this issue.

marthinal’s picture

Status: Needs work » Needs review

Oops, Sorry I forgot to comment that @dawhener told me to comment this change with FabianX.

dawehner’s picture

Is this change intentional? I don't think this fits into the scope of this issue.

Let me explain, where this is coming from.

So the settings links are part of core/modules/views_ui/templates/views-ui-display-tab-setting.html.twig, which has the following twig code:
{{ settings_links|safe_join('<span class="label">&nbsp;|&nbsp;</span>') }}

The links which are part of this issue, are put into that safe_join, and are now, after this change, a render array, so we need be able to render both a string and a render array in there as well.

lauriii’s picture

We need to document that change then.

dawehner’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

The inline template itself, looks fine, so all we need is a test

lauriii’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -Needs issue summary update

Needs work for tests

lauriii’s picture

Issue tags: +Needs beta evaluation
marthinal’s picture

working on the test.

Anonymous’s picture

This seems like a double from #2432759: views filter formatting in or group.

This issue probably has a better fix, but the other one already has some test coverage.

marthinal’s picture

Status: Needs work » Needs review
StatusFileSize
new2.14 KB
new4.96 KB

@pjonckiere thanks!

I think we need to create a FilterUITest and move the filter handler UI test there. So my test is different... also using test_filter_groups with this test.

The patch verifies if we have an array or not before escape.

The last submitted patch, 15: filters-2469563-15-only-test.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 15: filter-views_ui-2469563-15.patch, failed testing.

fabianx’s picture

  1. +++ b/core/modules/views_ui/src/ViewEditForm.php
    @@ -1143,7 +1143,15 @@ public function getFormBucket(ViewUI $view, $type, $display) {
    -            $store[$pid]['#link'] .= '&nbsp;&nbsp;' . ($group_info['groups'][$gid] == 'OR' ? $this->t('OR') : $this->t('AND'));
    +            $operator = ($group_info['groups'][$gid] == 'OR') ? $this->t('OR') : $this->t('AND');
    +            $store[$pid]['#link'] = array(
    ...
    +                'link' => $store[$pid]['#link']
    

    One way that would work without inline templates is:

    $store[$pid]['#link'] = SafeMarkup::format('!link &nbsp;&nbsp; !operator', ['!link' => $store[$pid]['#link'], '!operator' => $operator]);
    

    Because link should be safe to output and operator is safe to output (as it came via $this->t), the whole thing should work.

  2. +++ b/core/themes/engines/twig/twig.engine
    @@ -158,9 +158,14 @@ function twig_drupal_join_filter($value, $glue = '') {
    +      // Rendering the array in the case that we're using the inline_template.
    +      $output .= $separator . render($item);
    

    That's not the right fix.

    And I don't think we want to support that for drupal_join.

    This should be calling twig_drupal_escape_filter (which was recently moved to the twig extension).

    If we wanted to do that at all ...

marthinal’s picture

Status: Needs work » Needs review
StatusFileSize
new2.11 KB
new4.04 KB

@Fabianx thanks!

So let's try it then.

The last submitted patch, 19: filters-2469563-19-only-test.patch, failed testing.

Anonymous’s picture

An interdiff would have come in handy to see how you addressed the feedback from @Fabianx in #18 :)

Anyway, looks good to me. Some nitpicking though:

  1. +++ b/core/modules/views_ui/src/Tests/FilterUITest.php
    @@ -0,0 +1,74 @@
    +  protected function setUp()
    

    I think this needs @inheritdoc.

  2. +++ b/core/modules/views_ui/src/Tests/FilterUITest.php
    @@ -0,0 +1,74 @@
    +   * Tests that "Limit list to selected items" option is saved as expected when editing
    

    This wrapping should be 80 chars.

  3. +++ b/core/modules/views_ui/src/Tests/FilterUITest.php
    @@ -0,0 +1,74 @@
    +    $path = 'admin/structure/views/view/test_filter_groups';
    +    $this->drupalGet($path);
    

    I think this could be one line.

marthinal’s picture

@pjonckiere sure!

Please add author attribution to @pjonckiere and @b0unty because they were working on the same bug but from a different issue issue. Detected in #14. :)

The last submitted patch, 22: filters-2469563-22-only-test.patch, failed testing.

fabianx’s picture

Issue summary: View changes

Added proposed commit message.

fabianx’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

RTBC, looks great now.

xjm’s picture

Status: Reviewed & tested by the community » Needs review

That's a pretty ugly bug -- good find. Thanks for the test-only patch as well. I just have a couple questions about the patch:

  1. +++ b/core/modules/views_ui/src/Tests/FilterUITest.php
    @@ -0,0 +1,75 @@
    +   * Tests that "Limit list to selected items" option is saved as expected when
    +   * editing the Content type filter from the UI.
    

    Minor: This should be only a single line of fewer than 80 characters, so we can take a few words out or move details to a separate paragraph.

  2. +++ b/core/modules/views_ui/src/ViewEditForm.php
    @@ -1143,7 +1143,8 @@ public function getFormBucket(ViewUI $view, $type, $display) {
    -            $store[$pid]['#link'] .= '&nbsp;&nbsp;' . ($group_info['groups'][$gid] == 'OR' ? $this->t('OR') : $this->t('AND'));
    +            $operator = $group_info['groups'][$gid] == 'OR' ? $this->t('OR') : $this->t('AND');
    +            $store[$pid]['#link'] = SafeMarkup::format('!link &nbsp;&nbsp; !operator', ['!link' => $store[$pid]['#link'], '!operator' => $operator]);
    

    Why not just two separate full t()? I think the word order here could be affected by the language, which means we should give translators control over it, right? Rather than translating "AND" and "OR" separately with no context.

Also, per #22, I added credit for reviewers, but @b0unty has not commented on the issue so will need to be added to the commit message manually as per the summary.

marthinal’s picture

StatusFileSize
new4.27 KB
new2.25 KB

@xjm thanks for reviewing it :)

Well, maybe this is the way to give translators control over it...

marthinal’s picture

StatusFileSize
new4.26 KB
new1020 bytes

Short array syntax.

dawehner’s picture

It is odd that you can mark pretty much everything es safe by using t() in connection with SafeMarkup::format(). Honestly, I don't see the advantage over the previous solution of
using an inline template, in case there is one obvious one, please let's update the documentation .. Its not that we call that kind of code extremely often, it a rare operation on some admin UI.

marthinal’s picture

@dawehner The problem with the inline_template is that, in this case, we cannot render correctly.

at core/themes/engines/twig/twig.engine

$output .= $separator . SafeMarkup::escape($item);

We need render() instead SafeMarkup::escape() to render correctly.

To be honest I'm not sure why in other places we can use the inline_template and not here... I need to investigate about it...

Maybe @FabianX knows more about it...

xjm’s picture

Status: Needs review » Needs work
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -1143,7 +1143,8 @@ public function getFormBucket(ViewUI $view, $type, $display) {
-            $store[$pid]['#link'] .= '&nbsp;&nbsp;' . ($group_info['groups'][$gid] == 'OR' ? $this->t('OR') : $this->t('AND'));
...
+            $store[$pid]['#link'] = SafeMarkup::format('!link', ['!link' => $this->t('@link @operator', ['@link' => $store[$pid]['#link'], '@operator' => $this->t('&nbsp;&nbsp; @operator', ['@operator' => $operator])])]);

This is still translating the "or" and "and" twice and then formatting them again after that. We should just have one t() and no SafeMarkup::format() call. Like:

if ($whatever == 'OR') {
 $thing = $this->t('OR stuff');
}
else {
  $thing = $this->t('AND stuff');
}

No need to nest the calls; this makes it harder for translators.

marthinal’s picture

Status: Needs work » Needs review
StatusFileSize
new1.12 KB
new4.27 KB

@xjm Yes, makes no sense at all...

SafeMarkup::format

   * In most cases, you should use t() rather than calling this function
   * directly, since it will translate the text (on non-English-only sites) in
   * addition to formatting it.
dawehner’s picture

Status: Needs review » Reviewed & tested by the community

I still think that this is an abuse of t() as string concatenation but well.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -Needs tests, -Needs beta evaluation
  1. +++ b/core/modules/views_ui/src/ViewEditForm.php
    @@ -1143,7 +1143,8 @@ public function getFormBucket(ViewUI $view, $type, $display) {
    +            $operator = $group_info['groups'][$gid] == 'OR' ? $this->t('OR') : $this->t('AND');
    +            $store[$pid]['#link'] = SafeMarkup::format('!link', ['!link' => $this->t('@link @operator', ['@link' => $store[$pid]['#link'], '@operator' => $this->t('&nbsp;&nbsp; @operator', ['@operator' => $operator])])]);
               }
    

    Why can't we do the following...

      $operator = $group_info['groups'][$gid] == 'OR' ? $this->t('OR') : $this->t('AND');
      $store[$pid]['#link'] = SafeMarkup::format('@link &nbsp;&nbsp; @operator ', ['@link' => $store[$pid]['#link'], '@operator' => $operator]);
    

    I think this is better because '!link &nbsp;&nbsp; OR' is more likely to result in errors than translating 'OR'

  2. +++ b/core/modules/views_ui/src/Tests/FilterUITest.php
    @@ -0,0 +1,78 @@
    + * Contains \Drupal\views\Tests\Handler\FilterUITest.
    

    Should be... Contains \Drupal\views\Tests\FilterUITest

xjm’s picture

Status: Needs work » Needs review

@alexpott, did you read #26 and #32?

alexpott’s picture

Status: Needs review » Reviewed & tested by the community

@xjm pointed out that '!link &nbsp;&nbsp; OR' and '!link    AND' at least gives some context.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

This issue is a normal bug fix, and doesn't include any disruptive changes, so it is allowed per https://www.drupal.org/core/beta-changes. Committed 6b8cc66 and pushed to 8.0.x. Thanks!

diff --git a/core/modules/views_ui/src/Tests/FilterUITest.php b/core/modules/views_ui/src/Tests/FilterUITest.php
index 53a2d92..4d8f5fe 100644
--- a/core/modules/views_ui/src/Tests/FilterUITest.php
+++ b/core/modules/views_ui/src/Tests/FilterUITest.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\views\Tests\Handler\FilterUITest.
+ * Contains \Drupal\views\Tests\FilterUITest.
  */
 
 namespace Drupal\views_ui\Tests;

Fixed on commit.

  • alexpott committed 6b8cc66 on 8.0.x
    Issue #2469563 by marthinal, lauriii, dawehner, Fabianx, xjm, pjonckiere...
xjm’s picture

Status: Fixed » Needs review

For the record, @dawehner and I discussed the use of the t() in IRC. The correct thing to do would be to make the whole thing a template, but that's out of scope for fixing this bug.

My perspective is isn't an "abuse" of t(); it's exactly what t() is for -- to translate a word in context. In this case the context is short, but it is still a relevant. Languages do not translate word-for-word, and the little bit of context will help.

IMO the actual "abuse" is using &nbsp; for formatting in the first place, but having it inside a t() is better than not using t() with context for the link placeholder. (Edited for nonsense typos and clarity.)

xjm’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

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