diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-includes/kses.php --- a/wp/wp-includes/kses.php Tue Oct 22 16:11:46 2019 +0200 +++ b/wp/wp-includes/kses.php Tue Dec 15 13:49:49 2020 +0100 @@ -47,7 +47,7 @@ // Ensure that these variables are added to the global namespace // (e.g. if using namespaces / autoload in the current PHP environment). -global $allowedposttags, $allowedtags, $allowedentitynames; +global $allowedposttags, $allowedtags, $allowedentitynames, $allowedxmlentitynames; if ( ! CUSTOM_TAGS ) { /** @@ -230,6 +230,7 @@ 'border' => true, 'height' => true, 'hspace' => true, + 'loading' => true, 'longdesc' => true, 'vspace' => true, 'src' => true, @@ -397,15 +398,16 @@ ), 'var' => array(), 'video' => array( - 'autoplay' => true, - 'controls' => true, - 'height' => true, - 'loop' => true, - 'muted' => true, - 'poster' => true, - 'preload' => true, - 'src' => true, - 'width' => true, + 'autoplay' => true, + 'controls' => true, + 'height' => true, + 'loop' => true, + 'muted' => true, + 'playsinline' => true, + 'poster' => true, + 'preload' => true, + 'src' => true, + 'width' => true, ), ); @@ -703,6 +705,18 @@ 'there4', ); + /** + * @var string[] $allowedxmlentitynames Array of KSES allowed XML entitity names. + * @since 5.5.0 + */ + $allowedxmlnamedentities = array( + 'amp', + 'lt', + 'gt', + 'apos', + 'quot', + ); + $allowedposttags = array_map( '_wp_add_global_attributes', $allowedposttags ); } else { $allowedtags = wp_kses_array_lc( $allowedtags ); @@ -723,8 +737,9 @@ * @since 1.0.0 * * @param string $string Text content to filter. - * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, or a - * context name such as 'post'. + * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. See wp_kses_allowed_html() + * for the list of accepted context names. * @param string[] $allowed_protocols Array of allowed URL protocols. * @return string Filtered content containing only the allowed HTML. */ @@ -732,9 +747,11 @@ if ( empty( $allowed_protocols ) ) { $allowed_protocols = wp_allowed_protocols(); } + $string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) ); $string = wp_kses_normalize_entities( $string ); $string = wp_kses_hook( $string, $allowed_html, $allowed_protocols ); + return wp_kses_split( $string, $allowed_html, $allowed_protocols ); } @@ -775,12 +792,12 @@ // Remove quotes surrounding $value. // Also guarantee correct quoting in $string for this one attribute. - if ( '' == $value ) { + if ( '' === $value ) { $quote = ''; } else { $quote = $value[0]; } - if ( '"' == $quote || "'" == $quote ) { + if ( '"' === $quote || "'" === $quote ) { if ( substr( $value, -1 ) != $quote ) { return ''; } @@ -793,7 +810,7 @@ $value = esc_attr( $value ); // Sanitize URI values. - if ( in_array( strtolower( $name ), $uris ) ) { + if ( in_array( strtolower( $name ), $uris, true ) ) { $value = wp_kses_bad_protocol( $value, $allowed_protocols ); } @@ -896,20 +913,24 @@ * * @since 1.0.0 * - * @param string $string Content to filter through KSES. - * @param array[]|string $allowed_html List of allowed HTML elements. - * @param string[] $allowed_protocols Array of allowed URL protocols. + * @param string $string Content to filter through KSES. + * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. See wp_kses_allowed_html() + * for the list of accepted context names. + * @param string[] $allowed_protocols Array of allowed URL protocols. * @return string Filtered content through {@see 'pre_kses'} hook. */ function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) { /** - * Filters content to be run through kses. + * Filters content to be run through KSES. * * @since 2.3.0 * - * @param string $string Content to run through KSES. - * @param array[]|string $allowed_html Allowed HTML elements. - * @param string[] $allowed_protocols Array of allowed URL protocols. + * @param string $string Content to filter through KSES. + * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. See wp_kses_allowed_html() + * for the list of accepted context names. + * @param string[] $allowed_protocols Array of allowed URL protocols. */ return apply_filters( 'pre_kses', $string, $allowed_html, $allowed_protocols ); } @@ -932,23 +953,28 @@ * * @since 1.0.0 * - * @global array $pass_allowed_html - * @global array $pass_allowed_protocols + * @global array[]|string $pass_allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. + * @global string[] $pass_allowed_protocols Array of allowed URL protocols. * - * @param string $string Content to filter. - * @param array $allowed_html Allowed HTML elements. - * @param string[] $allowed_protocols Array of allowed URL protocols. + * @param string $string Content to filter. + * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. See wp_kses_allowed_html() + * for the list of accepted context names. + * @param string[] $allowed_protocols Array of allowed URL protocols. * @return string Content with fixed HTML tags */ function wp_kses_split( $string, $allowed_html, $allowed_protocols ) { global $pass_allowed_html, $pass_allowed_protocols; + $pass_allowed_html = $allowed_html; $pass_allowed_protocols = $allowed_protocols; + return preg_replace_callback( '%(|$))|(<[^>]*(>|$)|>)%', '_wp_kses_split_callback', $string ); } /** - * Helper function listing HTML attributes containing a URL. + * Returns an array of HTML attribute names whose value contains a URL. * * This function returns a list of all HTML attributes that must contain * a URL according to the HTML specification. @@ -959,7 +985,7 @@ * * @since 5.0.1 * - * @return array HTML attributes that must include a URL. + * @return string[] HTML attribute names whose value contains a URL. */ function wp_kses_uri_attributes() { $uri_attributes = array( @@ -990,7 +1016,7 @@ * * @since 5.0.1 * - * @param array $uri_attributes HTML attributes requiring validation as a URL. + * @param string[] $uri_attributes HTML attribute names whose value contains a URL. */ $uri_attributes = apply_filters( 'wp_kses_uri_attributes', $uri_attributes ); @@ -1004,13 +1030,16 @@ * @access private * @ignore * - * @global array $pass_allowed_html - * @global array $pass_allowed_protocols + * @global array[]|string $pass_allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. + * @global string[] $pass_allowed_protocols Array of allowed URL protocols. * + * @param array $matches preg_replace regexp matches * @return string */ function _wp_kses_split_callback( $match ) { global $pass_allowed_html, $pass_allowed_protocols; + return wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols ); } @@ -1030,31 +1059,33 @@ * @ignore * @since 1.0.0 * - * @param string $string Content to filter. - * @param array $allowed_html Allowed HTML elements. - * @param string[] $allowed_protocols Array of allowed URL protocols. + * @param string $string Content to filter. + * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. See wp_kses_allowed_html() + * for the list of accepted context names. + * @param string[] $allowed_protocols Array of allowed URL protocols. * @return string Fixed HTML element */ function wp_kses_split2( $string, $allowed_html, $allowed_protocols ) { $string = wp_kses_stripslashes( $string ); // It matched a ">" character. - if ( substr( $string, 0, 1 ) != '<' ) { + if ( '<' !== substr( $string, 0, 1 ) ) { return '>'; } // Allow HTML comments. - if ( '' ), '', $string ); - while ( $string != ( $newstring = wp_kses( $string, $allowed_html, $allowed_protocols ) ) ) { + while ( ( $newstring = wp_kses( $string, $allowed_html, $allowed_protocols ) ) != $string ) { $string = $newstring; } - if ( $string == '' ) { + if ( '' === $string ) { return ''; } - // prevent multiple dashes in comments + // Prevent multiple dashes in comments. $string = preg_replace( '/--+/', '-', $string ); - // prevent three dashes closing a comment + // Prevent three dashes closing a comment. $string = preg_replace( '/-$/', '', $string ); return ""; } @@ -1078,7 +1109,7 @@ } // No attributes are allowed for closing elements. - if ( $slash != '' ) { + if ( '' !== $slash ) { return ""; } @@ -1096,10 +1127,12 @@ * * @since 1.0.0 * - * @param string $element HTML element/tag. - * @param string $attr HTML attributes from HTML element to closing HTML element tag. - * @param array $allowed_html Allowed HTML elements. - * @param string[] $allowed_protocols Array of allowed URL protocols. + * @param string $element HTML element/tag. + * @param string $attr HTML attributes from HTML element to closing HTML element tag. + * @param array[]|string $allowed_html An array of allowed HTML elements and attributes, + * or a context name such as 'post'. See wp_kses_allowed_html() + * for the list of accepted context names. + * @param string[] $allowed_protocols Array of allowed URL protocols. * @return string Sanitized HTML element. */ function wp_kses_attr( $element, $attr, $allowed_html, $allowed_protocols ) { @@ -1119,11 +1152,11 @@ return "<$element$xhtml_slash>"; } - // Split it + // Split it. $attrarr = wp_kses_hair( $attr, $allowed_protocols ); // Go through $attrarr, and save the allowed attributes for this element - // in $attr2 + // in $attr2. $attr2 = ''; foreach ( $attrarr as $arreach ) { if ( wp_kses_attr_check( $arreach['name'], $arreach['value'], $arreach['whole'], $arreach['vless'], $element, $allowed_html ) ) { @@ -1131,7 +1164,7 @@ } } - // Remove any "<" or ">" characters + // Remove any "<" or ">" characters. $attr2 = preg_replace( '/[<>]/', '', $attr2 ); return "<$element$attr2$xhtml_slash>"; @@ -1152,10 +1185,19 @@ * @return bool Whether or not the attribute is allowed. */ function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowed_html ) { - $allowed_attr = $allowed_html[ strtolower( $element ) ]; + $name_low = strtolower( $name ); + $element_low = strtolower( $element ); - $name_low = strtolower( $name ); - if ( ! isset( $allowed_attr[ $name_low ] ) || '' == $allowed_attr[ $name_low ] ) { + if ( ! isset( $allowed_html[ $element_low ] ) ) { + $name = ''; + $value = ''; + $whole = ''; + return false; + } + + $allowed_attr = $allowed_html[ $element_low ]; + + if ( ! isset( $allowed_attr[ $name_low ] ) || '' === $allowed_attr[ $name_low ] ) { /* * Allow `data-*` attributes. * @@ -1173,16 +1215,20 @@ */ $allowed_attr[ $match[0] ] = $allowed_attr['data-*']; } else { - $name = $value = $whole = ''; + $name = ''; + $value = ''; + $whole = ''; return false; } } - if ( 'style' == $name_low ) { + if ( 'style' === $name_low ) { $new_value = safecss_filter_attr( $value ); if ( empty( $new_value ) ) { - $name = $value = $whole = ''; + $name = ''; + $value = ''; + $whole = ''; return false; } @@ -1191,10 +1237,12 @@ } if ( is_array( $allowed_attr[ $name_low ] ) ) { - // there are some checks + // There are some checks. foreach ( $allowed_attr[ $name_low ] as $currkey => $currval ) { if ( ! wp_kses_check_attr_val( $value, $vless, $currkey, $currval ) ) { - $name = $value = $whole = ''; + $name = ''; + $value = ''; + $whole = ''; return false; } } @@ -1226,30 +1274,31 @@ $attrname = ''; $uris = wp_kses_uri_attributes(); - // Loop through the whole attribute list + // Loop through the whole attribute list. while ( strlen( $attr ) != 0 ) { $working = 0; // Was the last operation successful? switch ( $mode ) { case 0: - if ( preg_match( '/^([-a-zA-Z:]+)/', $attr, $match ) ) { + if ( preg_match( '/^([_a-zA-Z][-_a-zA-Z0-9:.]*)/', $attr, $match ) ) { $attrname = $match[1]; - $working = $mode = 1; - $attr = preg_replace( '/^[-a-zA-Z:]+/', '', $attr ); + $working = 1; + $mode = 1; + $attr = preg_replace( '/^[_a-zA-Z][-_a-zA-Z0-9:.]*/', '', $attr ); } break; case 1: - if ( preg_match( '/^\s*=\s*/', $attr ) ) { // equals sign + if ( preg_match( '/^\s*=\s*/', $attr ) ) { // Equals sign. $working = 1; $mode = 2; $attr = preg_replace( '/^\s*=\s*/', '', $attr ); break; } - if ( preg_match( '/^\s+/', $attr ) ) { // valueless + if ( preg_match( '/^\s+/', $attr ) ) { // Valueless. $working = 1; $mode = 0; if ( false === array_key_exists( $attrname, $attrarr ) ) { @@ -1269,7 +1318,7 @@ if ( preg_match( '%^"([^"]*)"(\s+|/?$)%', $attr, $match ) ) { // "value" $thisval = $match[1]; - if ( in_array( strtolower( $attrname ), $uris ) ) { + if ( in_array( strtolower( $attrname ), $uris, true ) ) { $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); } @@ -1290,7 +1339,7 @@ if ( preg_match( "%^'([^']*)'(\s+|/?$)%", $attr, $match ) ) { // 'value' $thisval = $match[1]; - if ( in_array( strtolower( $attrname ), $uris ) ) { + if ( in_array( strtolower( $attrname ), $uris, true ) ) { $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); } @@ -1311,7 +1360,7 @@ if ( preg_match( "%^([^\s\"']+)(\s+|/?$)%", $attr, $match ) ) { // value $thisval = $match[1]; - if ( in_array( strtolower( $attrname ), $uris ) ) { + if ( in_array( strtolower( $attrname ), $uris, true ) ) { $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols ); } @@ -1330,17 +1379,17 @@ } break; - } // switch + } // End switch. - if ( $working == 0 ) { // not well formed, remove and try again + if ( 0 == $working ) { // Not well-formed, remove and try again. $attr = wp_kses_html_error( $attr ); $mode = 0; } - } // while + } // End while. - if ( $mode == 1 && false === array_key_exists( $attrname, $attrarr ) ) { - // special case, for when the attribute list ends with a valueless - // attribute like "selected" + if ( 1 == $mode && false === array_key_exists( $attrname, $attrarr ) ) { + // Special case, for when the attribute list ends with a valueless + // attribute like "selected". $attrarr[ $attrname ] = array( 'name' => $attrname, 'value' => '', @@ -1389,7 +1438,7 @@ $xhtml_slash = ''; } - // Split it + // Split it. $attrarr = wp_kses_hair_parse( $attr ); if ( false === $attrarr ) { return false; @@ -1422,25 +1471,25 @@ // phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation $regex = - '(?:' - . '[-a-zA-Z:]+' // Attribute name. - . '|' - . '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html. - . ')' - . '(?:' // Attribute value. - . '\s*=\s*' // All values begin with '=' - . '(?:' - . '"[^"]*"' // Double-quoted - . '|' - . "'[^']*'" // Single-quoted - . '|' - . '[^\s"\']+' // Non-quoted - . '(?:\s|$)' // Must have a space - . ')' - . '|' - . '(?:\s|$)' // If attribute has no value, space is required. - . ')' - . '\s*'; // Trailing space is optional except as mentioned above. + '(?:' + . '[_a-zA-Z][-_a-zA-Z0-9:.]*' // Attribute name. + . '|' + . '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html. + . ')' + . '(?:' // Attribute value. + . '\s*=\s*' // All values begin with '='. + . '(?:' + . '"[^"]*"' // Double-quoted. + . '|' + . "'[^']*'" // Single-quoted. + . '|' + . '[^\s"\']+' // Non-quoted. + . '(?:\s|$)' // Must have a space. + . ')' + . '|' + . '(?:\s|$)' // If attribute has no value, space is required. + . ')' + . '\s*'; // Trailing space is optional except as mentioned above. // phpcs:enable // Although it is possible to reduce this procedure to a single regexp, @@ -1476,9 +1525,11 @@ switch ( strtolower( $checkname ) ) { case 'maxlen': - // The maxlen check makes sure that the attribute value has a length not - // greater than the given value. This can be used to avoid Buffer Overflows - // in WWW clients and various Internet servers. + /* + * The maxlen check makes sure that the attribute value has a length not + * greater than the given value. This can be used to avoid Buffer Overflows + * in WWW clients and various Internet servers. + */ if ( strlen( $value ) > $checkvalue ) { $ok = false; @@ -1486,8 +1537,10 @@ break; case 'minlen': - // The minlen check makes sure that the attribute value has a length not - // smaller than the given value. + /* + * The minlen check makes sure that the attribute value has a length not + * smaller than the given value. + */ if ( strlen( $value ) < $checkvalue ) { $ok = false; @@ -1495,11 +1548,13 @@ break; case 'maxval': - // The maxval check does two things: it checks that the attribute value is - // an integer from 0 and up, without an excessive amount of zeroes or - // whitespace (to avoid Buffer Overflows). It also checks that the attribute - // value is not greater than the given value. - // This check can be used to avoid Denial of Service attacks. + /* + * The maxval check does two things: it checks that the attribute value is + * an integer from 0 and up, without an excessive amount of zeroes or + * whitespace (to avoid Buffer Overflows). It also checks that the attribute + * value is not greater than the given value. + * This check can be used to avoid Denial of Service attacks. + */ if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) { $ok = false; @@ -1510,8 +1565,10 @@ break; case 'minval': - // The minval check makes sure that the attribute value is a positive integer, - // and that it is not smaller than the given value. + /* + * The minval check makes sure that the attribute value is a positive integer, + * and that it is not smaller than the given value. + */ if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) { $ok = false; @@ -1522,16 +1579,18 @@ break; case 'valueless': - // The valueless check makes sure if the attribute has a value - // (like ``) or not (``) or not (`