diff --git a/core/modules/filter/src/Plugin/Filter/FilterHtml.php b/core/modules/filter/src/Plugin/Filter/FilterHtml.php index 0718b73092..71e234cbf6 100644 --- a/core/modules/filter/src/Plugin/Filter/FilterHtml.php +++ b/core/modules/filter/src/Plugin/Filter/FilterHtml.php @@ -2,6 +2,7 @@ namespace Drupal\filter\Plugin\Filter; +use Drupal\Component\Render\HtmlEscapedText; use Drupal\Component\Utility\Xss; use Drupal\Core\Form\FormStateInterface; use Drupal\Component\Utility\Html; @@ -53,6 +54,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { 'filter/drupal.filter.filter_html.admin', ], ], + '#element_validate' => [[$this, 'validateNoWildcardTag']], ]; $form['filter_html_help'] = [ '#type' => 'checkbox', @@ -67,6 +69,26 @@ public function settingsForm(array $form, FormStateInterface $form_state) { return $form; } + /** + * Validation callback for allowed_html: no wildcard tags allowed. + * + * @param array $element + * The form element whose value is being validated. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function validateNoWildcardTag(array $element, FormStateInterface $form_state) : void { + $allowed_html_value = $form_state->getValue($element['#parents']); + $matches = []; + if (preg_match_all('/<[^\s>]*\*[^\s>]*>/', $allowed_html_value, $matches)) { + $form_state->setError($element, $this->formatPlural( + count($matches[0]), + $this->t('The wildcard tag @allowed_tag_name is not supported.', ['@allowed_tag_name' => new HtmlEscapedText(reset($matches[0]))]), + $this->t('The wildcard tags @allowed_tag_names are not supported.', ['@allowed_tag_names' => new HtmlEscapedText(implode(' ', $matches[0]))]) + )); + } + } + /** * {@inheritdoc} */ @@ -278,6 +300,12 @@ public function setTextMode($textMode, $untilTag = NULL) { $xpath = new \DOMXPath($dom); foreach ($xpath->query('//body//*') as $node) { $tag = $node->tagName; + // Skip wildcard tags: those are not allowed. Config cannot be trusted to + // have been validated. + // @see ::validateNoWildcardTag() + if (strpos($tag, $star_protector) !== FALSE) { + continue; + } // All attributes are already allowed on this tag, this is the most // permissive configuration, no additional processing is required. diff --git a/core/modules/filter/tests/src/Functional/FilterAdminTest.php b/core/modules/filter/tests/src/Functional/FilterAdminTest.php index 7d2cf73a04..7d6de080e3 100644 --- a/core/modules/filter/tests/src/Functional/FilterAdminTest.php +++ b/core/modules/filter/tests/src/Functional/FilterAdminTest.php @@ -382,6 +382,32 @@ public function testUrlFilterAdmin() { $this->assertSession()->statusMessageNotContains('The text format Basic HTML has been updated.'); } + /** + * Tests the HTML filter settings form is properly validated. + */ + public function testHtmlFilterAdmin() { + $selector = 'filters[filter_html][settings][allowed_html]'; + + $this->drupalGet('admin/config/content/formats/manage/basic_html'); + $page = $this->getSession()->getPage(); + $original_value = $page->findField($selector)->getValue(); + + // Assert validation error when trying to allow `<*>`. + $page->fillField($selector, $original_value . ' <*>'); + $page->findButton('Save configuration')->click(); + $this->assertSession()->statusMessageContains('The wildcard tag <*> is not supported.', 'error'); + + // Assert validation error when trying to allow `<*> `. + $page->fillField($selector, $original_value . ' <*> '); + $page->findButton('Save configuration')->click(); + $this->assertSession()->statusMessageContains('The wildcard tags <*> are not supported.', 'error'); + + // Assert validation error does no trip over wildcard attributes. + $page->fillField($selector, $original_value . ' <*>
'); + $page->findButton('Save configuration')->click(); + $this->assertSession()->statusMessageContains('The wildcard tags <*> are not supported.', 'error'); + } + /** * Tests whether filter tips page is not HTML escaped. */ diff --git a/core/modules/filter/tests/src/Unit/FilterHtmlTest.php b/core/modules/filter/tests/src/Unit/FilterHtmlTest.php index 1c3e3f054d..ca4f8d3fcc 100644 --- a/core/modules/filter/tests/src/Unit/FilterHtmlTest.php +++ b/core/modules/filter/tests/src/Unit/FilterHtmlTest.php @@ -93,4 +93,114 @@ public function testSetConfiguration() { $this->assertSame('

', $filter->getConfiguration()['settings']['allowed_html']); } + /** + * @covers ::getHTMLRestrictions + * @dataProvider providerGetHtmlRestrictions + */ + public function testGetHtmlRestrictions(string $allowed_html, array $expected_html_restrictions) { + $filter = new FilterHtml(['settings' => ['allowed_html' => $allowed_html]], 'filter_html', ['provider' => 'test']); + $this->assertSame($expected_html_restrictions, $filter->getHTMLRestrictions()); + } + + /** + * @return \Generator + */ + public function providerGetHtmlRestrictions() { + $hardcoded_asterisk_restrictions = [ + 'style' => FALSE, + 'on*' => FALSE, + 'lang' => TRUE, + 'dir' => [ + 'ltr' => TRUE, + 'rtl' => TRUE, + ], + ]; + + yield 'no allowed tags' => [ + '', + [ + 'allowed' => [ + '*' => $hardcoded_asterisk_restrictions, + ], + ], + ]; + + yield '


' => [ + '


', + [ + 'allowed' => [ + 'p' => FALSE, + 'br' => FALSE, + '*' => $hardcoded_asterisk_restrictions, + ], + ], + ]; + + yield '


' => [ + '


', + [ + 'allowed' => [ + 'p' => [ + 'class' => TRUE, + ], + 'br' => FALSE, + '*' => $hardcoded_asterisk_restrictions, + ], + ], + ]; + + yield '


' => [ + '


', + [ + 'allowed' => [ + 'p' => [ + 'class' => [ + 'foo' => TRUE, + 'bar' => TRUE, + ], + ], + 'br' => FALSE, + '*' => $hardcoded_asterisk_restrictions, + ], + ], + ]; + + yield '


' => [ + '


', + [ + 'allowed' => [ + 'p' => [ + 'class' => [ + 'foo' => TRUE, + 'bar' => TRUE, + ], + 'data-*' => TRUE, + ], + 'br' => FALSE, + '*' => $hardcoded_asterisk_restrictions, + ], + ], + ]; + + yield '<* class="foo bar">
' => [ + '<* class="foo bar">
', + [ + 'allowed' => [ + 'br' => FALSE, + '*' => $hardcoded_asterisk_restrictions, + ], + ], + ]; + + yield '
' => [ + '
', + [ + 'allowed' => [ + 'br' => FALSE, + '*' => $hardcoded_asterisk_restrictions, + ], + ], + ]; + } + }