diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index 7d7f84b..2078e4b 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -33,6 +33,25 @@ class Xss {
protected static $htmlTags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd');
/**
+ * The default list of safe attributes untouched by filter().
+ *
+ * @var array
+ *
+ * @see \Drupal\Component\Utility\Xss::filter()
+ */
+ protected static $safeAttributes = ['alt', 'title'];
+
+ /**
+ * The default list of RDFa attributes untouched by filter().
+ *
+ * @var array
+ *
+ * @see \Drupal\Component\Utility\Xss::filter()
+ * @see http://www.w3.org/TR/xhtml-rdfa/
+ */
+ protected static $rdfaAttributes = ['property', 'typeof', 'rel', 'rev', 'datatype'];
+
+ /**
* Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
*
* Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses.
@@ -230,11 +249,7 @@ protected static function attributes($attributes) {
// such attributes.
// @see \Drupal\Component\Utility\UrlHelper::filterBadProtocol()
// @see http://www.w3.org/TR/html4/index/attributes.html
- $skip_protocol_filtering = substr($attribute_name, 0, 5) === 'data-' || in_array($attribute_name, array(
- 'title',
- 'alt',
- 'typeof',
- ));
+ $skip_protocol_filtering = substr($attribute_name, 0, 5) === 'data-' || in_array($attribute_name, static::$safeAttributes);
$working = $mode = 1;
$attributes = preg_replace('/^[-a-zA-Z]+/', '', $attributes);
@@ -261,7 +276,12 @@ protected static function attributes($attributes) {
case 2:
// Attribute value, a URL after href= for instance.
if (preg_match('/^"([^"]*)"(\s+|$)/', $attributes, $match)) {
- $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ if (in_array($attribute_name, static::$rdfaAttributes)) {
+ $thisval = ($skip_protocol_filtering || preg_match('/^[[:alnum:]]+\:[[:alnum:]]+$/', $match[1])) ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ }
+ else {
+ $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ }
if (!$skip) {
$attributes_array[] = "$attribute_name=\"$thisval\"";
@@ -273,7 +293,12 @@ protected static function attributes($attributes) {
}
if (preg_match("/^'([^']*)'(\s+|$)/", $attributes, $match)) {
- $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ if (in_array($attribute_name, static::$rdfaAttributes)) {
+ $thisval = ($skip_protocol_filtering || preg_match('/^[[:alnum:]]+\:[[:alnum:]]+$/', $match[1])) ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ }
+ else {
+ $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ }
if (!$skip) {
$attributes_array[] = "$attribute_name='$thisval'";
@@ -284,7 +309,12 @@ protected static function attributes($attributes) {
}
if (preg_match("%^([^\s\"']+)(\s+|$)%", $attributes, $match)) {
- $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ if (in_array($attribute_name, static::$rdfaAttributes)) {
+ $thisval = ($skip_protocol_filtering || preg_match('/^[[:alnum:]]+\:[[:alnum:]]+$/', $match[1])) ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ }
+ else {
+ $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+ }
if (!$skip) {
$attributes_array[] = "$attribute_name=\"$thisval\"";
@@ -353,4 +383,5 @@ public static function getAdminTagList() {
public static function getHtmlTagList() {
return static::$htmlTags;
}
+
}
diff --git a/core/tests/Drupal/Tests/Component/Utility/XssTest.php b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
index a212112..d838eb5 100644
--- a/core/tests/Drupal/Tests/Component/Utility/XssTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
@@ -516,6 +516,42 @@ public function providerTestAttributes() {
'Image tag with data attribute',
array('img')
),
+ array(
+ '
',
+ '
',
+ 'Image tag with RDFa with namespaced attribute',
+ array('img')
+ ),
+ array(
+ '
',
+ '
',
+ 'Image tag with RDFa with bad with namespaced attribute',
+ array('img')
+ ),
+ array(
+ '
',
+ '
',
+ 'Image tag with non-RDFa attribute',
+ array('img')
+ ),
+ array(
+ '
';
- $this->assertEquals(Xss::filter($img1, ['img']), $img1, 'Attributes OK');
- $img2 = '
';
- $this->assertEquals(Xss::filter($img2, ['img']), $img2, 'Attributes OK');
- }
-
- /**
* Data provider for testFilterXssAdminNotNormalized().
*
* @see testFilterXssAdminNotNormalized()