diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php index 69d12fe..fdf8110 100644 --- a/core/lib/Drupal/Component/Utility/SafeMarkup.php +++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php @@ -329,4 +329,43 @@ public static function replace($search, $replace, $subject) { } } + /** + * Truncates an HTML string safely to a number of plain-text characters. + * + * Functions similarly to \Drupal\Component\Utility\Unicode::truncate(), + * but strips HTML tags prior to truncating and ensures the truncated string + * has well-formed HTML entities. + * + * @param string $string + * The string to truncate. HTML tags are stripped prior to truncation, but + * HTML entities that fit in the output length are preserved. + * @param int $max_length + * An upper limit on the returned string length, including trailing ellipsis + * if $add_ellipsis is TRUE. + * @param bool $wordsafe + * If TRUE, attempt to truncate on a word boundary. See Unicode::truncate() + * for details. + * @param bool $add_ellipsis + * If TRUE, add '...' to the end of the truncated string (defaults to + * FALSE). The string length will still fall within $max_length. + * @param int $min_wordsafe_length + * If $wordsafe is TRUE, the minimum acceptable length for truncation (before + * adding an ellipsis, if $add_ellipsis is TRUE). See Unicode::truncate() + * for details. + * + * @return string + * The truncated string. + */ + public static function truncate($string, $max_length, $wordsafe = FALSE, $add_ellipsis = FALSE, $min_wordsafe_length = 1) { + // Strip tags prior to truncation, so that the output has useful text. + $string = Xss::filterNoSafeMarkUp($string, []); + + // Truncate. + $string = Unicode::truncate($string, $max_length, $wordsafe, $add_ellipsis, $min_wordsafe_length); + + // The truncation might have split an HTML entity, so invoke Xss::filter() + // to correct that. + return Xss::filter($string, []); + } + } diff --git a/core/lib/Drupal/Component/Utility/Unicode.php b/core/lib/Drupal/Component/Utility/Unicode.php index 7691e00..4938b0d 100644 --- a/core/lib/Drupal/Component/Utility/Unicode.php +++ b/core/lib/Drupal/Component/Utility/Unicode.php @@ -508,7 +508,7 @@ public static function substr($text, $start, $length = NULL) { * @param bool $add_ellipsis * If TRUE, add '...' to the end of the truncated string (defaults to * FALSE). The string length will still fall within $max_length. - * @param bool $min_wordsafe_length + * @param int $min_wordsafe_length * If $wordsafe is TRUE, the minimum acceptable length for truncation (before * adding an ellipsis, if $add_ellipsis is TRUE). Has no effect if $wordsafe * is FALSE. This can be used to prevent having a very short resulting string diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php index 4c6e812..af4f145 100644 --- a/core/lib/Drupal/Component/Utility/Xss.php +++ b/core/lib/Drupal/Component/Utility/Xss.php @@ -26,9 +26,6 @@ class Xss { /** * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities. * - * Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses. - * For examples of various XSS attacks, see: http://ha.ckers.org/xss.html. - * * This code does five things: * - Removes characters and constructs that can trick browsers. * - Makes sure all HTML entities are well-formed. @@ -48,12 +45,48 @@ class Xss { * An XSS safe version of $string, or an empty string if $string is not * valid UTF-8. * + * @see \Drupal\Component\Utility\Unicode::filterNoSafeMarkUp() * @see \Drupal\Component\Utility\Unicode::validateUtf8() * @see \Drupal\Component\Utility\SafeMarkup * * @ingroup sanitization */ public static function filter($string, $html_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) { + $string = static::filterNoSafeMarkUp($string, $html_tags); + return SafeMarkup::set($string); + } + + + /** + * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities. + * + * Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses. + * For examples of various XSS attacks, see: http://ha.ckers.org/xss.html. + * + * This code does four things: + * - Removes characters and constructs that can trick browsers. + * - Makes sure all HTML entities are well-formed. + * - Makes sure all HTML tags and attributes are well-formed. + * - Makes sure no HTML tags contain URLs with a disallowed protocol (e.g. + * javascript:). + * + * @param $string + * The string with raw HTML in it. It will be stripped of everything that + * can cause an XSS attack. + * @param array $html_tags + * An array of HTML tags. + * + * @return string + * An XSS safe version of $string, or an empty string if $string is not + * valid UTF-8. + * + * @see \Drupal\Component\Utility\Xss::filter() + * @see \Drupal\Component\Utility\Unicode::validateUtf8() + * + * + * @ingroup sanitization + */ + public static function filterNoSafeMarkUp($string, $html_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) { // Only operate on valid UTF-8 strings. This is necessary to prevent cross // site scripting issues on Internet Explorer 6. if (!Unicode::validateUtf8($string)) { @@ -83,7 +116,7 @@ public static function filter($string, $html_tags = array('a', 'em', 'strong', ' // for output. All other known XSS vectors have been filtered out by this // point and any HTML tags remaining will have been deliberately allowed, so // it is acceptable to call SafeMarkup::set() on the resultant string. - return SafeMarkup::set(preg_replace_callback('% + return preg_replace_callback('% ( <(?=[^a-zA-Z!/]) # a lone < | # or @@ -92,7 +125,7 @@ public static function filter($string, $html_tags = array('a', 'em', 'strong', ' <[^>]*(>|$) # a string that starts with a <, up until the > or the end of the string | # or > # just a > - )%x', $splitter, $string)); + )%x', $splitter, $string); } /** diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php index 2e2eccf..fd22e46 100644 --- a/core/modules/dblog/src/Controller/DbLogController.php +++ b/core/modules/dblog/src/Controller/DbLogController.php @@ -185,9 +185,7 @@ public function overview() { $message = $this->formatMessage($dblog); if ($message && isset($dblog->wid)) { // Truncate link_text to 56 chars of message. - // @todo Reevaluate the SafeMarkup::set() in - // https://www.drupal.org/node/2399261. - $log_text = SafeMarkup::set(Unicode::truncate(Xss::filter($message, array()), 56, TRUE, TRUE)); + $log_text = SafeMarkup::truncate($message, 56, TRUE, TRUE); $message = $this->l($log_text, new Url('dblog.event', array('event_id' => $dblog->wid), array( 'attributes' => array( // Provide a title for the link for useful hover hints.