Commit 97ea6b56 authored by catch's avatar catch

Issue #2551511 by alexpott, stefan.r, RavindraSingh, pjonckiere, JeroenT,...

Issue #2551511 by alexpott, stefan.r, RavindraSingh, pjonckiere, JeroenT, Mariano: Remove SafeMarkup::set() from AllowedTagsXssTrait
parent ba95bbb2
......@@ -6,12 +6,13 @@
namespace Drupal\Core\Field;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
/**
* Useful methods when dealing with displaying allowed tags.
*
* @deprecated in Drupal 8.0.x, will be removed before Drupal 9.0.0. Use
* \Drupal\Core\Field\FieldFilteredString instead.
*
* @see \Drupal\Core\Field\FieldFilteredString
*/
trait AllowedTagsXssTrait {
......@@ -33,30 +34,21 @@ trait AllowedTagsXssTrait {
* valid UTF-8.
*/
public function fieldFilterXss($string) {
// All known XSS vectors are filtered out by
// \Drupal\Component\Utility\Xss::filter(), all tags in the markup are
// allowed intentionally by the trait, and no danger is added in by
// \Drupal\Component\Utility\HTML::normalize(). Since the normalized value
// is essentially the same markup, designate this string as safe as well.
// This method is an internal part of field sanitization, so the resultant,
// sanitized string should be printable as is.
//
// @todo Free this memory in https://www.drupal.org/node/2505963.
return SafeMarkup::set(Html::normalize(Xss::filter($string, $this->allowedTags())));
return FieldFilteredString::create($string);
}
/**
* Returns a list of tags allowed by AllowedTagsXssTrait::fieldFilterXss().
*/
public function allowedTags() {
return array('a', 'b', 'big', 'code', 'del', 'em', 'i', 'ins', 'pre', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'tt', 'ol', 'ul', 'li', 'p', 'br', 'img');
return FieldFilteredString::allowedTags();
}
/**
* Returns a human-readable list of allowed tags for display in help texts.
*/
public function displayAllowedTags() {
return '<' . implode('> <', $this->allowedTags()) . '>';
return FieldFilteredString::displayAllowedTags();
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldFilteredString.
*/
namespace Drupal\Core\Field;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeStringInterface;
use Drupal\Component\Utility\SafeStringTrait;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\Xss;
/**
* Defines an object that passes safe strings through the Field system.
*
* This object filters the string using a very restrictive tag list when it is
* created.
*
* @internal
* This object is marked as internal because it should only be used by the
* Field module and field-related plugins.
*
* @see \Drupal\Core\Render\SafeString
*/
final class FieldFilteredString implements SafeStringInterface, \Countable {
use SafeStringTrait;
/**
* Overrides \Drupal\Component\Utility\SafeStringTrait::create().
*
* @return string|\Drupal\Component\Utility\SafeStringInterface
* A safe string filtered with the allowed tag list and normalized.
*
* @see \Drupal\Core\Field\FieldFilteredString::allowedTags()
* @see \Drupal\Component\Utility\Xss::filter()
* @see \Drupal\Component\Utility\Html::normalize()
*/
public static function create($string) {
$string = (string) $string;
if ($string === '') {
return '';
}
$safe_string = new static();
// All known XSS vectors are filtered out by
// \Drupal\Component\Utility\Xss::filter(), all tags in the markup are
// allowed intentionally by the trait, and no danger is added in by
// \Drupal\Component\Utility\HTML::normalize(). Since the normalized value
// is essentially the same markup, designate this string as safe as well.
// This method is an internal part of field sanitization, so the resultant,
// sanitized string should be printable as is.
$safe_string->string = Html::normalize(Xss::filter($string, static::allowedTags()));
return $safe_string;
}
/**
* Returns the allowed tag list.
*
* @return string[]
* A list of allowed tags.
*/
public static function allowedTags() {
return ['a', 'b', 'big', 'code', 'del', 'em', 'i', 'ins', 'pre', 'q', 'small', 'span', 'strong', 'sub', 'sup', 'tt', 'ol', 'ul', 'li', 'p', 'br', 'img'];
}
/**
* Returns a human-readable list of allowed tags for display in help texts.
*
* @return string
* A human-readable list of allowed tags for display in help texts.
*/
public static function displayAllowedTags() {
return '<' . implode('> <', static::allowedTags()) . '>';
}
}
......@@ -75,8 +75,8 @@ public function viewElements(FieldItemListInterface $items) {
// Account for prefix and suffix.
if ($this->getSetting('prefix_suffix')) {
$prefixes = isset($settings['prefix']) ? array_map(array($this, 'fieldFilterXss'), explode('|', $settings['prefix'])) : array('');
$suffixes = isset($settings['suffix']) ? array_map(array($this, 'fieldFilterXss'), explode('|', $settings['suffix'])) : array('');
$prefixes = isset($settings['prefix']) ? array_map(array('Drupal\Core\Field\FieldFilteredString', 'create'), explode('|', $settings['prefix'])) : array('');
$suffixes = isset($settings['suffix']) ? array_map(array('Drupal\Core\Field\FieldFilteredString', 'create'), explode('|', $settings['suffix'])) : array('');
$prefix = (count($prefixes) > 1) ? $this->formatPlural($item->value, $prefixes[0], $prefixes[1]) : $prefixes[0];
$suffix = (count($suffixes) > 1) ? $this->formatPlural($item->value, $suffixes[0], $suffixes[1]) : $suffixes[0];
$output = $prefix . $output . $suffix;
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
......@@ -101,11 +102,11 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
// Add prefix and suffix.
if ($field_settings['prefix']) {
$prefixes = explode('|', $field_settings['prefix']);
$element['#field_prefix'] = $this->fieldFilterXss(array_pop($prefixes));
$element['#field_prefix'] = FieldFilteredString::create(array_pop($prefixes));
}
if ($field_settings['suffix']) {
$suffixes = explode('|', $field_settings['suffix']);
$element['#field_suffix'] = $this->fieldFilterXss(array_pop($suffixes));
$element['#field_suffix'] = FieldFilteredString::create(array_pop($suffixes));
}
return array('value' => $element);
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
......@@ -190,7 +191,7 @@ protected function supportsGroups() {
*/
protected function sanitizeLabel(&$label) {
// Allow a limited set of HTML tags.
$label = $this->fieldFilterXss($label);
$label = FieldFilteredString::create($label);
}
/**
......
......@@ -86,7 +86,7 @@ public function form(FieldItemListInterface $items, array &$form, FormStateInter
$delta = isset($get_delta) ? $get_delta : 0;
$element = array(
'#title' => SafeMarkup::checkPlain($this->fieldDefinition->getLabel()),
'#description' => $this->fieldFilterXss(\Drupal::token()->replace($this->fieldDefinition->getDescription())),
'#description' => FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())),
);
$element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
......@@ -165,7 +165,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
}
$title = SafeMarkup::checkPlain($this->fieldDefinition->getLabel());
$description = $this->fieldFilterXss(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
$description = FieldFilteredString::create(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
$elements = array();
......
......@@ -10,6 +10,7 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\field\FieldConfigInterface;
......@@ -65,7 +66,7 @@ public function form(array $form, FormStateInterface $form_state) {
'#title' => $this->t('Help text'),
'#default_value' => $this->entity->getDescription(),
'#rows' => 5,
'#description' => $this->t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => $this->displayAllowedTags())) . '<br />' . $this->t('This field supports tokens.'),
'#description' => $this->t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => FieldFilteredString::displayAllowedTags())) . '<br />' . $this->t('This field supports tokens.'),
'#weight' => -10,
);
......
......@@ -7,6 +7,7 @@
use Drupal\Component\Utility\Html;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Render\Element;
/**
......@@ -167,7 +168,7 @@ function template_preprocess_file_upload_help(&$variables) {
$descriptions = array();
if (!empty($description)) {
$descriptions[] = Html::normalize($description);
$descriptions[] = FieldFilteredString::create($description);
}
if (isset($cardinality)) {
if ($cardinality == -1) {
......
......@@ -10,6 +10,7 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\WidgetBase;
......@@ -119,7 +120,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
}
$title = SafeMarkup::checkPlain($this->fieldDefinition->getLabel());
$description = $this->fieldFilterXss($this->fieldDefinition->getDescription());
$description = FieldFilteredString::create($this->fieldDefinition->getDescription());
$elements = array();
......
......@@ -97,7 +97,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f
if ($cardinality == 1) {
// If there's only one field, return it as delta 0.
if (empty($elements[0]['#default_value']['fids'])) {
$file_upload_help['#description'] = $this->fieldFilterXss($this->fieldDefinition->getDescription());
$file_upload_help['#description'] = $this->fieldDefinition->getDescription();
$elements[0]['#description'] = \Drupal::service('renderer')->renderPlain($file_upload_help);
}
}
......
......@@ -8,6 +8,7 @@
namespace Drupal\options\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\OptGroup;
......@@ -48,7 +49,10 @@ public function viewElements(FieldItemListInterface $items) {
// If the stored value is in the current set of allowed values, display
// the associated label, otherwise just display the raw value.
$output = isset($options[$value]) ? $options[$value] : $value;
$elements[$delta] = array('#markup' => $this->fieldFilterXss($output));
$elements[$delta] = array(
'#markup' => $output,
'#allowed_tags' => FieldFilteredString::allowedTags(),
);
}
}
......
......@@ -8,6 +8,7 @@
namespace Drupal\options\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
......@@ -35,7 +36,10 @@ public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($items as $delta => $item) {
$elements[$delta] = array('#markup' => $this->fieldFilterXss($item->value));
$elements[$delta] = array(
'#markup' => $item->value,
'#allowed_tags' => FieldFilteredString::allowedTags(),
);
}
return $elements;
......
......@@ -9,6 +9,7 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\ViewExecutable;
......@@ -80,7 +81,7 @@ public function summaryName($data) {
$value = $data->{$this->name_alias};
// If the list element has a human readable name show it.
if (isset($this->allowedValues[$value]) && !empty($this->options['summary']['human'])) {
return $this->fieldFilterXss($this->allowedValues[$value]);
return FieldFilteredString::create($this->allowedValues[$value]);
}
// Else, fallback to the key.
else {
......
......@@ -8,6 +8,7 @@
namespace Drupal\options\Plugin\views\argument;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\ViewExecutable;
......@@ -80,12 +81,9 @@ public function summaryName($data) {
$value = $data->{$this->name_alias};
// If the list element has a human readable name show it.
if (isset($this->allowedValues[$value]) && !empty($this->options['summary']['human'])) {
return $this->caseTransform($this->fieldFilterXss($this->allowedValues[$value]), $this->options['case']);
}
// Else, fallback to the key.
else {
return $this->caseTransform(SafeMarkup::checkPlain($value), $this->options['case']);
$value = $this->allowedValues[$value];
}
return FieldFilteredString::create($this->caseTransform($value, $this->options['case']));
}
}
......@@ -38,7 +38,7 @@ public function testFormatter() {
$build = $items->view(array('type' => 'list_key'));
$this->assertEqual($build['#formatter'], 'list_key', 'The chosen formatter is used.');
$this->assertEqual($build[0]['#markup'], 1);
$this->assertEqual((string) $build[0]['#markup'], 1);
}
}
......@@ -9,6 +9,7 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
......@@ -70,7 +71,7 @@ public function summaryName($data) {
$value = $data->{$this->name_alias};
// If the list element has a human readable name show it,
if (isset($this->allowed_values[$value]) && !empty($this->options['summary']['human'])) {
return $this->fieldFilterXss($this->allowed_values[$value]);
return FieldFilteredString::create($this->allowed_values[$value]);
}
// else fallback to the key.
else {
......
......@@ -9,6 +9,7 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Field\AllowedTagsXssTrait;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
......@@ -72,12 +73,9 @@ public function summaryName($data) {
$value = $data->{$this->name_alias};
// If the list element has a human readable name show it,
if (isset($this->allowed_values[$value]) && !empty($this->options['summary']['human'])) {
return $this->caseTransform($this->fieldfilterXss($this->allowed_values[$value]), $this->options['case']);
}
// else fallback to the key.
else {
return $this->caseTransform(SafeMarkup::checkPlain($value), $this->options['case']);
$value = $this->allowed_values[$value];
}
return FieldFilteredString::create($this->caseTransform($value, $this->options['case']));
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Field\FieldFilteredStringTest.
*/
namespace Drupal\Tests\Core\Field;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Field\FieldFilteredString;
use Drupal\Component\Utility\SafeStringInterface;
/**
* @coversDefaultClass \Drupal\Core\Field\FieldFilteredString
* @group Field
*/
class FieldFilteredStringTest extends UnitTestCase {
/**
* @covers ::create
* @dataProvider providerTestCreate
*/
public function testCreate($string, $expected, $instance_of_check) {
$filtered_string = FieldFilteredString::create($string);
if ($instance_of_check) {
$this->assertInstanceOf(FieldFilteredString::class, $filtered_string);
}
$this->assertSame($expected, (string) $filtered_string);
}
/**
* Provides data for testCreate().
*/
public function providerTestCreate() {
$data = [];
$data[] = ['', '', FALSE];
// Certain tags are filtered.
$data[] = ['<script>teststring</script>', 'teststring', TRUE];
// Certain tags are not filtered.
$data[] = ['<em>teststring</em>', '<em>teststring</em>', TRUE];
// HTML will be normalized.
$data[] = ['<em>teststring', '<em>teststring</em>', TRUE];
// Even safe strings will be escaped.
$safe_string = $this->prophesize(SafeStringInterface::class);
$safe_string->__toString()->willReturn('<script>teststring</script>');
$data[] = [$safe_string->reveal(), 'teststring', TRUE];
return $data;
}
/**
* @covers: ::displayAllowedTags
*/
public function testdisplayAllowedTags() {
$expected = '<a> <b> <big> <code> <del> <em> <i> <ins> <pre> <q> <small> <span> <strong> <sub> <sup> <tt> <ol> <ul> <li> <p> <br> <img>';
$this->assertSame($expected, FieldFilteredString::displayAllowedTags());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment