Whitelist additional WAI-ARIA attributes, and all role values

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
This commit is contained in:
Matt Fitzpatrick 2016-01-17 17:19:26 -08:00 committed by Tim Starling
parent 7c4bd85d21
commit 1397863569
3 changed files with 75 additions and 36 deletions

View 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 ===

View 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',
);

View 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' ),
);
}
}