Issue #2550945 by alexpott, joelpittet, xjm, Wim Leers, stefan.r: Add Html::escape()
parent
9594553dee
commit
a5bfd122e1
|
@ -338,14 +338,54 @@ EOD;
|
|||
* "<", not "<"). Be careful when using this function, as it will revert
|
||||
* previous sanitization efforts (<script> will become <script>).
|
||||
*
|
||||
* This method is not the opposite of Html::escape(). For example, this method
|
||||
* will convert "é" to "é", whereas Html::escape() will not convert "é"
|
||||
* to "é".
|
||||
*
|
||||
* @param string $text
|
||||
* The text to decode entities in.
|
||||
*
|
||||
* @return string
|
||||
* The input $text, with all HTML entities decoded once.
|
||||
*
|
||||
* @see html_entity_decode()
|
||||
* @see \Drupal\Component\Utility\Html::escape()
|
||||
*/
|
||||
public static function decodeEntities($text) {
|
||||
return html_entity_decode($text, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes text by converting special characters to HTML entities.
|
||||
*
|
||||
* This method escapes HTML for sanitization purposes by replacing the
|
||||
* following special characters with their HTML entity equivalents:
|
||||
* - & (ampersand) becomes &
|
||||
* - " (double quote) becomes "
|
||||
* - ' (single quote) becomes '
|
||||
* - < (less than) becomes <
|
||||
* - > (greater than) becomes >
|
||||
* Special characters that have already been escaped will be double-escaped
|
||||
* (for example, "<" becomes "&lt;").
|
||||
*
|
||||
* This method is not the opposite of Html::decodeEntities(). For example,
|
||||
* this method will not encode "é" to "é", whereas
|
||||
* Html::decodeEntities() will convert all HTML entities to UTF-8 bytes,
|
||||
* including "é" and "<" to "é" and "<".
|
||||
*
|
||||
* @param string $text
|
||||
* The input text.
|
||||
*
|
||||
* @return string
|
||||
* The text with all HTML special characters converted.
|
||||
*
|
||||
* @see htmlspecialchars()
|
||||
* @see \Drupal\Component\Utility\Html::decodeEntities()
|
||||
*
|
||||
* @ingroup sanitization
|
||||
*/
|
||||
public static function escape($text) {
|
||||
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ class SafeMarkup {
|
|||
* @see drupal_validate_utf8()
|
||||
*/
|
||||
public static function checkPlain($text) {
|
||||
$string = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
|
||||
$string = Html::escape($text);
|
||||
static::$safeStrings[$string]['html'] = TRUE;
|
||||
return $string;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Render\SafeString;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
|
@ -78,7 +79,7 @@ class HtmlTag extends RenderElement {
|
|||
|
||||
// An HTML tag should not contain any special characters. Escape them to
|
||||
// ensure this cannot be abused.
|
||||
$escaped_tag = htmlspecialchars($element['#tag'], ENT_QUOTES, 'UTF-8');
|
||||
$escaped_tag = Html::escape($element['#tag']);
|
||||
$markup = '<' . $escaped_tag . $attributes;
|
||||
// Construct a void element.
|
||||
if (in_array($element['#tag'], self::$voidElements)) {
|
||||
|
|
|
@ -40,7 +40,9 @@ use Drupal\Component\Utility\SafeStringInterface;
|
|||
* @endcode
|
||||
*
|
||||
* The attribute keys and values are automatically sanitized for output with
|
||||
* htmlspecialchars() and the entire attribute string is marked safe for output.
|
||||
* Html::escape() and the entire attribute string is marked safe for output.
|
||||
*
|
||||
* @see \Drupal\Component\Utility\Html::escape()
|
||||
*/
|
||||
class Attribute implements \ArrayAccess, \IteratorAggregate, SafeStringInterface {
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\Core\Template;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* A class that defines a type of Attribute that can be added to as an array.
|
||||
*
|
||||
|
@ -74,7 +76,7 @@ class AttributeArray extends AttributeValueBase implements \ArrayAccess, \Iterat
|
|||
public function __toString() {
|
||||
// Filter out any empty values before printing.
|
||||
$this->value = array_unique(array_filter($this->value));
|
||||
return htmlspecialchars(implode(' ', $this->value), ENT_QUOTES, 'UTF-8');
|
||||
return Html::escape(implode(' ', $this->value));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\Core\Template;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* A class that defines a type of boolean HTML attribute.
|
||||
*
|
||||
|
@ -40,7 +42,7 @@ class AttributeBoolean extends AttributeValueBase {
|
|||
* Implements the magic __toString() method.
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->value === FALSE ? '' : htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8');
|
||||
return $this->value === FALSE ? '' : Html::escape($this->name);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\Core\Template;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* A class that represents most standard HTML attributes.
|
||||
*
|
||||
|
@ -28,7 +30,7 @@ class AttributeString extends AttributeValueBase {
|
|||
* Implements the magic __toString() method.
|
||||
*/
|
||||
public function __toString() {
|
||||
return htmlspecialchars($this->value, ENT_QUOTES, 'UTF-8');
|
||||
return Html::escape($this->value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
namespace Drupal\Core\Template;
|
||||
use Drupal\Component\Utility\Html;
|
||||
|
||||
/**
|
||||
* Defines the base class for an attribute type.
|
||||
|
@ -55,7 +56,7 @@ abstract class AttributeValueBase {
|
|||
public function render() {
|
||||
$value = (string) $this;
|
||||
if (isset($this->value) && static::RENDER_EMPTY_ATTRIBUTE || !empty($value)) {
|
||||
return htmlspecialchars($this->name, ENT_QUOTES, 'UTF-8') . '="' . $value . '"';
|
||||
return Html::escape($this->name) . '="' . $value . '"';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\aggregator\Tests;
|
||||
|
||||
use Drupal\aggregator\Entity\Feed;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\aggregator\FeedInterface;
|
||||
|
||||
|
@ -243,7 +244,7 @@ abstract class AggregatorTestBase extends WebTestBase {
|
|||
public function getValidOpml(array $feeds) {
|
||||
// Properly escape URLs so that XML parsers don't choke on them.
|
||||
foreach ($feeds as &$feed) {
|
||||
$feed['url[0][value]'] = htmlspecialchars($feed['url[0][value]']);
|
||||
$feed['url[0][value]'] = Html::escape($feed['url[0][value]']);
|
||||
}
|
||||
/**
|
||||
* Does not have an XML declaration, must pass the parser.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Menu;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
|
@ -30,8 +31,8 @@ class LocalActionTest extends WebTestBase {
|
|||
// Ensure that both menu and route based actions are shown.
|
||||
$this->assertLocalAction([
|
||||
[Url::fromRoute('menu_test.local_action4'), 'My dynamic-title action'],
|
||||
[Url::fromRoute('menu_test.local_action4'), htmlspecialchars("<script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')],
|
||||
[Url::fromRoute('menu_test.local_action4'), htmlspecialchars("<script>alert('Welcome to the derived jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')],
|
||||
[Url::fromRoute('menu_test.local_action4'), Html::escape("<script>alert('Welcome to the jungle!')</script>")],
|
||||
[Url::fromRoute('menu_test.local_action4'), Html::escape("<script>alert('Welcome to the derived jungle!')</script>")],
|
||||
[Url::fromRoute('menu_test.local_action2'), 'My hook_menu action'],
|
||||
[Url::fromRoute('menu_test.local_action3'), 'My YAML discovery action'],
|
||||
[Url::fromRoute('menu_test.local_action5'), 'Title override'],
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Menu;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
|
@ -78,9 +79,9 @@ class LocalTasksTest extends WebTestBase {
|
|||
]);
|
||||
|
||||
// Verify that script tags are escaped on output.
|
||||
$title = htmlspecialchars("Task 1 <script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$title = Html::escape("Task 1 <script>alert('Welcome to the jungle!')</script>");
|
||||
$this->assertLocalTaskAppers($title);
|
||||
$title = htmlspecialchars("<script>alert('Welcome to the derived jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$title = Html::escape("<script>alert('Welcome to the derived jungle!')</script>");
|
||||
$this->assertLocalTaskAppers($title);
|
||||
|
||||
// Verify that local tasks appear as defined in the router.
|
||||
|
@ -92,7 +93,7 @@ class LocalTasksTest extends WebTestBase {
|
|||
['menu_test.local_task_test_tasks_settings_dynamic', []],
|
||||
]);
|
||||
|
||||
$title = htmlspecialchars("<script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$title = Html::escape("<script>alert('Welcome to the jungle!')</script>");
|
||||
$this->assertLocalTaskAppers($title);
|
||||
|
||||
// Ensure the view tab is active.
|
||||
|
|
|
@ -259,4 +259,54 @@ class HtmlTest extends UnitTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Html::escape().
|
||||
*
|
||||
* @dataProvider providerEscape
|
||||
* @covers ::escape
|
||||
*/
|
||||
public function testEscape($expected, $text) {
|
||||
$this->assertEquals($expected, Html::escape($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testEscape().
|
||||
*
|
||||
* @see testCheckPlain()
|
||||
*/
|
||||
public function providerEscape() {
|
||||
return array(
|
||||
array('Drupal', 'Drupal'),
|
||||
array('<script>', '<script>'),
|
||||
array('&lt;script&gt;', '<script>'),
|
||||
array('&#34;', '"'),
|
||||
array('"', '"'),
|
||||
array('&quot;', '"'),
|
||||
array(''', "'"),
|
||||
array('&#039;', '''),
|
||||
array('©', '©'),
|
||||
array('→', '→'),
|
||||
array('➼', '➼'),
|
||||
array('€', '€'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests relationship between escaping and decoding HTML entities.
|
||||
*
|
||||
* @covers ::decodeEntities
|
||||
* @covers ::escape
|
||||
*/
|
||||
public function testDecodeEntitiesAndEscape() {
|
||||
$string = "<em>répété</em>";
|
||||
$escaped = Html::escape($string);
|
||||
$this->assertSame('<em>répét&eacute;</em>', $escaped);
|
||||
$decoded = Html::decodeEntities($escaped);
|
||||
$this->assertSame('<em>répété</em>', $decoded);
|
||||
$decoded = Html::decodeEntities($decoded);
|
||||
$this->assertSame('<em>répété</em>', $decoded);
|
||||
$escaped = Html::escape($decoded);
|
||||
$this->assertSame('<em>répété</em>', $escaped);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* Handles integration of Twig templates with the Drupal theme system.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Render\SafeString;
|
||||
use Drupal\Core\Extension\Extension;
|
||||
|
@ -75,7 +76,7 @@ function twig_render_template($template_file, array $variables) {
|
|||
}
|
||||
if ($twig_service->isDebug()) {
|
||||
$output['debug_prefix'] .= "\n\n<!-- THEME DEBUG -->";
|
||||
$output['debug_prefix'] .= "\n<!-- THEME HOOK: '" . htmlspecialchars($variables['theme_hook_original'], ENT_QUOTES, 'UTF-8') . "' -->";
|
||||
$output['debug_prefix'] .= "\n<!-- THEME HOOK: '" . Html::escape($variables['theme_hook_original']) . "' -->";
|
||||
// If there are theme suggestions, reverse the array so more specific
|
||||
// suggestions are shown first.
|
||||
if (!empty($variables['theme_hook_suggestions'])) {
|
||||
|
@ -109,10 +110,10 @@ function twig_render_template($template_file, array $variables) {
|
|||
$prefix = ($template == $current_template) ? 'x' : '*';
|
||||
$suggestion = $prefix . ' ' . $template;
|
||||
}
|
||||
$output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n " . htmlspecialchars(implode("\n ", $suggestions), ENT_QUOTES, 'UTF-8') . "\n-->";
|
||||
$output['debug_info'] .= "\n<!-- FILE NAME SUGGESTIONS:\n " . Html::escape(implode("\n ", $suggestions)) . "\n-->";
|
||||
}
|
||||
$output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '" . htmlspecialchars($template_file, ENT_QUOTES, 'UTF-8') . "' -->\n";
|
||||
$output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . htmlspecialchars($template_file, ENT_QUOTES, 'UTF-8') . "' -->\n\n";
|
||||
$output['debug_info'] .= "\n<!-- BEGIN OUTPUT from '" . Html::escape($template_file) . "' -->\n";
|
||||
$output['debug_suffix'] .= "\n<!-- END OUTPUT from '" . Html::escape($template_file) . "' -->\n\n";
|
||||
}
|
||||
// This output has already been rendered and is therefore considered safe.
|
||||
return SafeString::create(implode('', $output));
|
||||
|
|
Loading…
Reference in New Issue