Problem/Motivation

Currently code does:

$escaped_separator = Xss::filterAdmin($this->options['separator']);

(e.g. for #2280961: (Views)FieldPluginBase::advancedRender() calls SafeMarkup::set() on a string that it doesn't know to be safe))

However this is problematic as that means a malicious admin user can essentially get e.g. '>' marked as safe with that.

While in this special case, probably '>' should be converted to > there are other cases where safe_join is called with something that needs to be safe and HTML.

So while it is of great convenience that functions like filterAdminXss call SafeMarkup::set() we still need to ensure that we never set e.g. something lower than 3 characters as safe and b) we should add an API function to SafeMarkup that can be called to escape something, but do _not_ mark it as safe.

Proposed resolution

- Add a SafeMarkup::filterSeparator function to take that task
- Possibly use the filterSeparator function from safe_join by default, so that no one inputs

alert('xss')

as separator.
- Consider not marking safe any strings that are lower than 3 characters.

This is critical due to the security implications.

Remaining tasks

- Discuss solutions

User interface changes

TBD

API changes

TBD

Comments

chx’s picture

> Consider not marking safe any strings that are lower than 3 characters.

Most excellent suggestion!

cilefen’s picture

Status: Active » Needs review
StatusFileSize
new3.15 KB

This is just a sketch.

Status: Needs review » Needs work

The last submitted patch, 2: create-2383009-2.patch, failed testing.

cilefen’s picture

Status: Needs work » Needs review
StatusFileSize
new445 bytes
new3.15 KB
dawehner’s picture

index 56bd518..8f52e5d 100644
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php

--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php

@@ -153,6 +153,17 @@ public static function checkAdminXss($string) {
   /**
+   * @todo Document this function.
+   *
+   * @param string $string
+   *
+   * @return SafeMarkup|string
+   */
+  public static function filterSeparator($string) {
+    return twig_drupal_join_filter(array($string));
+  }
+

Mh, so we add a dependency from a component into a drupal specific function, not sure whether this is right and it should not be the other way round. Move the code of twig_drupal_join_filter here

On top of that, it could be also helpful to write a dedicated test for it.

dawehner’s picture

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

.

cilefen’s picture

Status: Needs work » Needs review
StatusFileSize
new2.03 KB
new4.67 KB

Status: Needs review » Needs work

The last submitted patch, 7: create-2383009-7.patch, failed testing.

cilefen’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new2.24 KB
new4.9 KB
dawehner’s picture

+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -153,6 +153,21 @@ public static function checkAdminXss($string) {
+   *
+   * Strings smaller than 3 characters will not be set as safe.
+   *

We should explain why would never be a problem: ... there is no single html tag which has less than 3 chars: <b> ... already needs 3

cilefen’s picture

StatusFileSize
new898 bytes
new5 KB

@dawehner: Thank you for reviewing.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Great!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
    @@ -101,4 +101,23 @@ public function testInvalidSetMultiple() {
    +  /**
    +   * Tests SafeMarkup::filterSeparator().
    +   *
    +   * @covers ::filterSeparator
    +   */
    +  public function testFilterSeparator() {
    +    $returned = SafeMarkup::filterSeparator('<br />');
    +    $this->assertEquals('<br />', $returned, 'Separator string "<br />" was not modified.');
    +    $this->assertTrue(SafeMarkup::isSafe($returned), 'Separator string "<br />" was marked as safe');
    +    $returned = SafeMarkup::filterSeparator('---');
    +    $this->assertEquals('---', $returned, 'Separator string "---" was not modified');
    +    $this->assertTrue(SafeMarkup::isSafe($returned), 'Separator string "---" was marked as safe');
    +    $returned = SafeMarkup::filterSeparator('<');
    +    $this->assertEquals('<', $returned, 'Separator string "<" was not modified');
    +    $this->assertFalse(SafeMarkup::isSafe($returned), 'Separator string "<" was not marked as safe');
    +    $returned = SafeMarkup::filterSeparator('<script>alert("hello");</script>');
    +    $this->assertEquals('alert("hello");', $returned, 'Separator string "<script>alert("hello");</script>" was modified');
    +    $this->assertTrue(SafeMarkup::isSafe($returned), 'Modified separator string "alert("hello");" was marked as safe');
    +  }
    

    This test would be neater and more maintainable as a provider / test combination. But we can do that in followup as the patch adds the necessary test coverage.

  2. +++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
    @@ -153,6 +153,23 @@ public static function checkAdminXss($string) {
    +    return strlen($string) < 3 ? $string : Xss::filterAdmin($string);
    

    Unicode::strlen()? And should we not move this further up into SafeMarkup::set()?

  3. What about twig_drupal_join_filter()? Shouldn't that use this?
cilefen’s picture

Status: Needs work » Needs review
StatusFileSize
new962 bytes
new5.5 KB

This is Unicode::strlen() and calling SafeMarkup::filterSeparator() from twig_drupal_join_filter(). There is still a problem here: twig_drupal_join_filter() sets the whole string as safe even though SafeMarkup::filterSeparator() may have considered the separator unsafe (such as "<").

As for whether we should move this to SafeMarkup::set(), perhaps.

Another issue is that in the first place we had been and still are using Xss:filterAdmin(), which itself would replace a lone "<" even if that was the actual character desired for the separator. It isn't clear if we want to allow that.

Status: Needs review » Needs work

The last submitted patch, 14: create-2383009-14.patch, failed testing.

cilefen’s picture

I suppose SafeMarkup::filterSeparator() could return an escaped string if less than 3 characters.

Status: Needs work » Needs review

cilefen queued 14: create-2383009-14.patch for re-testing.

cilefen’s picture

Issue summary: View changes
davidhernandez’s picture

I cannot reproduce a problem. I created Views with separators and the separators were escaped properly, including greater than or less than signs.

Note that depending on how you view the source code of a page, the less than/greater than might get processed and displayed as symbol.

cilefen’s picture

Xss::filterAdmin does not mark '>' as safe, it escapes it, and marks that as safe. I tested this against HEAD and '>' gets escaped. Can we close this?

subhojit777’s picture

Issue tags: +Needs reroll
jain_deepak’s picture

Issue tags: +SprintWeekend2015
StatusFileSize
new4.78 KB

Rerolled

manningpete’s picture

Issue tags: -Needs reroll
jibran’s picture

Status: Needs review » Needs work

#NW for #13.3

alexpott’s picture

Priority: Critical » Major
Issue tags: +Triaged D8 critical

Dicussed with @xjm, @webchick, @catch, @effulgentsia. Based on #19 and #20 and looking at twig_drupal_join_filter() I think we can downgrade this issue.

Leaving to @cilefen to confirm if this can be closed.

xjm’s picture

Issue tags: -Triaged D8 critical
cilefen’s picture

Status: Needs work » Closed (cannot reproduce)

From the summary:

However this is problematic as that means a malicious admin user can essentially get e.g. '>' marked as safe with that.

Manual testing showed that partial tags as separators are escaped, and this is why:

Field separators are passed through Xss::filterAdmin which restricts the allowed tags to 'a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd' by calling Xss:filter which in turn replaces offending partial tags.