Whitelist additional WAI-ARIA attributes, and all role values
authorMatt Fitzpatrick <mattfitzpat@sbcglobal.net>
Mon, 18 Jan 2016 01:19:26 +0000 (17:19 -0800)
committerTim Starling <tstarling@wikimedia.org>
Thu, 28 Jan 2016 00:29:40 +0000 (00:29 +0000)
Adds the attributes aria-describedby, aria-flowto, aria-label,
aria-labelledby, and aria-owns to the attribute whitelist for all
elements.

Adds Sanitizer::escapeIdReferenceList() to escape attributes
containing space delimited HTML id lists, in the same manner as
Sanitizer::escapeId().

Removes the role="presentation" restriction. Allows all values for
the role attribute.

Bug: T26659
Change-Id: I3a29d727c61f46ac115ca2e50fcb14deeea34418

RELEASE-NOTES-1.27
includes/Sanitizer.php
tests/phpunit/includes/SanitizerTest.php

index 50d40a6..b48d0b6 100644 (file)
@@ -146,6 +146,10 @@ production.
 * Added MWRestrictions as a class to check restrictions on a WebRequest, e.g.
   to assert that the request comes from a particular IP range.
 * Added bot passwords, a rights-restricted login mechanism for API-using bots.
+* Whitelisted the following HTML attributes for all elements in wikitext:
+  aria-describedby, aria-flowto, aria-label, aria-labelledby, aria-owns.
+* Removed "presentation" restriction on the HTML role attribute in wikitext.
+  All values are now allowed for the role attribute.
 
 === External library changes in 1.27 ===
 
index d41e559..e2564cd 100644 (file)
@@ -721,7 +721,7 @@ class Sanitizer {
         * Take an array of attribute names and values and normalize or discard
         * illegal values for the given whitelist.
         *
-        * - Discards attributes not the given whitelist
+        * - Discards attributes not on the given whitelist
         * - Unsafe style attributes are discarded
         * - Invalid id attributes are re-encoded
         *
@@ -770,18 +770,18 @@ class Sanitizer {
                                $value = Sanitizer::checkCss( $value );
                        }
 
+                       # Escape HTML id attributes
                        if ( $attribute === 'id' ) {
                                $value = Sanitizer::escapeId( $value, 'noninitial' );
                        }
 
-                       # WAI-ARIA
-                       # http://www.w3.org/TR/wai-aria/
-                       # http://www.whatwg.org/html/elements.html#wai-aria
-                       # For now we only support role="presentation" until we work out what roles should be
-                       # usable by content and we ensure that our code explicitly rejects patterns that
-                       # violate HTML5's ARIA restrictions.
-                       if ( $attribute === 'role' && $value !== 'presentation' ) {
-                               continue;
+                       # Escape HTML id reference lists
+                       if ( $attribute === 'aria-describedby'
+                               || $attribute === 'aria-flowto'
+                               || $attribute === 'aria-labelledby'
+                               || $attribute === 'aria-owns'
+                       ) {
+                               $value = Sanitizer::escapeIdReferenceList( $value, 'noninitial' );
                        }
 
                        // RDFa and microdata properties allow URLs, URIs and/or CURIs.
@@ -1163,6 +1163,39 @@ class Sanitizer {
                return $id;
        }
 
+       /**
+        * Given a string containing a space delimited list of ids, escape each id
+        * to match ids escaped by the escapeId() function.
+        *
+        * @since 1.27
+        *
+        * @param string $referenceString Space delimited list of ids
+        * @param string|array $options String or array of strings (default is array()):
+        *   'noninitial': This is a non-initial fragment of an id, not a full id,
+        *       so don't pay attention if the first character isn't valid at the
+        *       beginning of an id.  Only matters if $wgExperimentalHtmlIds is
+        *       false.
+        *   'legacy': Behave the way the old HTML 4-based ID escaping worked even
+        *       if $wgExperimentalHtmlIds is used, so we can generate extra
+        *       anchors and links won't break.
+        * @return string
+        */
+       static function escapeIdReferenceList( $referenceString, $options = array() ) {
+               # Explode the space delimited list string into an array of tokens
+               $references = preg_split( '/\s+/', "{$referenceString}", -1, PREG_SPLIT_NO_EMPTY );
+
+               # Escape each token as an id
+               foreach ( $references as &$ref ) {
+                       $ref = Sanitizer::escapeId( $ref, $options );
+               }
+
+               # Merge the array back to a space delimited list string
+               # If the array is empty, the result will be an empty string ('')
+               $referenceString = implode( ' ', $references );
+
+               return $referenceString;
+       }
+
        /**
         * Given a value, escape it so that it can be used as a CSS class and
         * return it.
@@ -1546,6 +1579,11 @@ class Sanitizer {
                        'title',
 
                        # WAI-ARIA
+                       'aria-describedby',
+                       'aria-flowto',
+                       'aria-label',
+                       'aria-labelledby',
+                       'aria-owns',
                        'role',
                );
 
index d3dc512..ca1ec50 100644 (file)
@@ -317,33 +317,6 @@ class SanitizerTest extends MediaWikiTestCase {
                );
        }
 
-       /**
-        * Test for support or lack of support for specific attributes in the attribute whitelist.
-        */
-       public static function provideAttributeSupport() {
-               /** array( <attributes>, <expected>, <message> ) */
-               return array(
-                       array(
-                               'div',
-                               ' role="presentation"',
-                               ' role="presentation"',
-                               'Support for WAI-ARIA\'s role="presentation".'
-                       ),
-                       array( 'div', ' role="main"', '', "Other WAI-ARIA roles are currently not supported." ),
-               );
-       }
-
-       /**
-        * @dataProvider provideAttributeSupport
-        * @covers Sanitizer::fixTagAttributes
-        */
-       public function testAttributeSupport( $tag, $attributes, $expected, $message ) {
-               $this->assertEquals( $expected,
-                       Sanitizer::fixTagAttributes( $attributes, $tag ),
-                       $message
-               );
-       }
-
        /**
         * @dataProvider provideEscapeHtmlAllowEntities
         * @covers Sanitizer::escapeHtmlAllowEntities
@@ -363,4 +336,28 @@ class SanitizerTest extends MediaWikiTestCase {
                        array( '&lt;script&gt;foo&lt;/script&gt;', '<script>foo</script>' ),
                );
        }
+
+       /**
+        * Test escapeIdReferenceList for consistency with escapeId
+        *
+        * @dataProvider provideEscapeIdReferenceList
+        * @covers Sanitizer::escapeIdReferenceList
+        */
+       public function testEscapeIdReferenceList( $referenceList, $id1, $id2 ) {
+               $this->assertEquals(
+                       Sanitizer::escapeIdReferenceList( $referenceList, 'noninitial' ),
+                       Sanitizer::escapeId( $id1, 'noninitial' )
+                               . ' '
+                               . Sanitizer::escapeId( $id2, 'noninitial' )
+               );
+       }
+
+       public static function provideEscapeIdReferenceList() {
+               /** array( <reference list>, <individual id 1>, <individual id 2> ) */
+               return array(
+                       array( 'foo bar', 'foo', 'bar' ),
+                       array( '#1 #2', '#1', '#2' ),
+                       array( '+1 +2', '+1', '+2' ),
+               );
+       }
 }