* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @package MediaWiki
- * @subpackage Parser
+ * @addtogroup Parser
*/
/**
'zwj' => 8205,
'zwnj' => 8204 );
-/** @package MediaWiki */
class Sanitizer {
/**
* Cleans up HTML, removes dangerous tags and attributes, and
static function removeHTMLtags( $text, $processCallback = null, $args = array() ) {
global $wgUseTidy, $wgUserHtml;
- static $htmlpairs, $htmlsingle, $htmlsingleonly, $htmlnest, $tabletags,
+ static $htmlpairs, $htmlsingle, $htmlsingleonly, $htmlnest, $tabletags,
$htmllist, $listtags, $htmlsingleallowed, $htmlelements, $staticInitialised;
-
+
wfProfileIn( __METHOD__ );
-
+
if ( !$staticInitialised ) {
if( $wgUserHtml ) {
$htmlpairs = array( # Tags that must be closed
'table', 'tr', 'td', 'th', 'div', 'blockquote', 'ol', 'ul',
'dl', 'font', 'big', 'small', 'sub', 'sup', 'span'
);
- $tabletags = array( # Can only appear inside table
+ $tabletags = array( # Can only appear inside table, we will close them
'td', 'th', 'tr',
);
$htmllist = array( # Tags used by list
# Remove HTML comments
$text = Sanitizer::removeHTMLcomments( $text );
$bits = explode( '<', $text );
- $text = array_shift( $bits );
+ $text = str_replace( '>', '>', array_shift( $bits ) );
if(!$wgUseTidy) {
$tagstack = $tablestack = array();
foreach ( $bits as $x ) {
- $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
- preg_match( '!^(/?)(\\w+)([^>]*?)(/{0,1}>)([^<]*)$!', $x, $regs );
- list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
- error_reporting( $prev );
+ $regs = array();
+ if( preg_match( '!^(/?)(\\w+)([^>]*?)(/{0,1}>)([^<]*)$!', $x, $regs ) ) {
+ list( /* $qbar */, $slash, $t, $params, $brace, $rest ) = $regs;
+ } else {
+ $slash = $t = $params = $brace = $rest = null;
+ }
$badtag = 0 ;
if ( isset( $htmlelements[$t = strtolower( $t )] ) ) {
} else if( isset( $htmlsingle[$t] ) ) {
# Hack to not close $htmlsingle tags
$brace = NULL;
+ } else if( isset( $tabletags[$t] )
+ && in_array($t ,$tagstack) ) {
+ // New table tag but forgot to close the previous one
+ $text .= "</$t>";
} else {
if ( $t == 'table' ) {
array_push( $tablestack, $tagstack );
}
if ( ! $badtag ) {
$rest = str_replace( '>', '>', $rest );
- $close = ( $brace == '/>' ) ? ' /' : '';
+ $close = ( $brace == '/>' && !$slash ) ? ' /' : '';
$text .= "<$slash$t$newparams$close>$rest";
continue;
}
foreach ( $bits as $x ) {
preg_match( '/^(\\/?)(\\w+)([^>]*?)(\\/{0,1}>)([^<]*)$/',
$x, $regs );
- @list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
+ @list( /* $qbar */, $slash, $t, $params, $brace, $rest ) = $regs;
if ( isset( $htmlelements[$t = strtolower( $t )] ) ) {
if( is_callable( $processCallback ) ) {
call_user_func_array( $processCallback, array( &$params, $args ) );
if( trim( $text ) == '' ) {
return '';
}
-
+
$stripped = Sanitizer::validateTagAttributes(
Sanitizer::decodeTagAttributes( $text ), $element );
-
+
$attribs = array();
foreach( $stripped as $attribute => $value ) {
$encAttribute = htmlspecialchars( $attribute );
$encValue = Sanitizer::safeEncodeAttribute( $value );
-
+
$attribs[] = "$encAttribute=\"$encValue\"";
}
return count( $attribs ) ? ' ' . implode( ' ', $attribs ) : '';
*/
static function encodeAttribute( $text ) {
$encValue = htmlspecialchars( $text );
-
+
// Whitespace is normalized during attribute decoding,
// so if we've been passed non-spaces we must encode them
// ahead of time or they won't be preserved.
"\r" => ' ',
"\t" => '	',
) );
-
+
return $encValue;
}
-
+
/**
* Encode an attribute value for HTML tags, with extra armoring
* against further wiki processing.
*/
static function safeEncodeAttribute( $text ) {
$encValue = Sanitizer::encodeAttribute( $text );
-
+
# Templates and links may be expanded in later parsing,
# creating invalid or dangerous output. Suppress this.
$encValue = strtr( $encValue, array(
* Given a value, escape it so that it can be used as a CSS class and
* return it.
*
- * TODO: For extra validity, input should be validated UTF-8.
+ * @todo For extra validity, input should be validated UTF-8.
*
* @link http://www.w3.org/TR/CSS21/syndata.html Valid characters/format
*
foreach( $pairs as $set ) {
$attribute = strtolower( $set[1] );
$value = Sanitizer::getTagAttributeCallback( $set );
-
+
// Normalize whitespace
$value = preg_replace( '/[\t\r\n ]+/', ' ', $value );
$value = trim( $value );
-
+
// Decode character references
$attribs[$attribute] = Sanitizer::decodeCharReferences( $value );
}
$out .= "]>\n";
return $out;
}
-
+
static function cleanUrl( $url, $hostname=true ) {
# Normalize any HTML entities in input. They will be
# re-escaped by makeExternalLink().
# Escape any control characters introduced by the above step
$url = preg_replace( '/[\][<>"\\x00-\\x20\\x7F]/e', "urlencode('\\0')", $url );
-
+
# Validate hostname portion
+ $matches = array();
if( preg_match( '!^([^:]+:)(//[^/]+)?(.*)$!iD', $url, $matches ) ) {
- list( $whole, $protocol, $host, $rest ) = $matches;
-
+ list( /* $whole */, $protocol, $host, $rest ) = $matches;
+
// Characters that will be ignored in IDNs.
// http://tools.ietf.org/html/3454#section-3.1
// Strip them before further processing so blacklists and such work.
\xe2\x80\x8d| # 200d ZERO WIDTH JOINER
[\xef\xb8\x80-\xef\xb8\x8f] # fe00-fe00f VARIATION SELECTOR-1-16
/xuD";
-
+
$host = preg_replace( $strip, '', $host );
-
+
// @fixme: validate hostnames here
-
+
return $protocol . $host . $rest;
} else {
return $url;