Make WordPress Core

Changeset 46907 for branches/5.1


Ignore:
Timestamp:
12/12/2019 06:36:20 PM (7 years ago)
Author:
whyisjake
Message:

Ensure that a user can publish_posts before making a post sticky.
Props: danielbachhuber, whyisjake, peterwilson, xknown.
Prevent stored XSS through wp_targeted_link_rel().
Props: vortfu, whyisjake, peterwilsoncc, xknown, SergeyBiryukov, flaviozavan.
Update wp_kses_bad_protocol() to recognize : on uri attributes,
wp_kses_bad_protocol() makes sure to validate that uri attributes don't contain invalid/or not allowed protocols. While this works fine in most cases, there's a risk that by using the colon html5 named entity, one is able to bypass this function.
Brings r46895 to the 5.3 branch.
Props: xknown, nickdaugherty, peterwilsoncc.
Prevent stored XSS in the block editor.
Brings r46896 to the 5.3 branch.
Prevent escaped unicode characters become unescaped in unsafe HTML during JSON decoding.
Props: aduth, epiqueras.

Location:
branches/5.1
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • branches/5.1

  • branches/5.1/src/wp-includes/blocks.php

    r44576 r46907  
    7575 * @see parse_blocks()
    7676 *
    77  * @param string                  $block_type Full Block type to look for.
     77 * @param string                  $block_name Full Block type to look for.
    7878 * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
    7979 * @return bool Whether the post content contains the specified block.
    8080 */
    81 function has_block( $block_type, $post = null ) {
     81function has_block( $block_name, $post = null ) {
    8282        if ( ! has_blocks( $post ) ) {
    8383                return false;
     
    9191        }
    9292
    93         return false !== strpos( $post, '<!-- wp:' . $block_type . ' ' );
     93        /*
     94         * Normalize block name to include namespace, if provided as non-namespaced.
     95         * This matches behavior for WordPress 5.0.0 - 5.3.0 in matching blocks by
     96         * their serialized names.
     97         */
     98        if ( false === strpos( $block_name, '/' ) ) {
     99                $block_name = 'core/' . $block_name;
     100        }
     101
     102        // Test for existence of block by its fully qualified name.
     103        $has_block = false !== strpos( $post, '<!-- wp:' . $block_name . ' ' );
     104
     105        if ( ! $has_block ) {
     106                /*
     107                 * If the given block name would serialize to a different name, test for
     108                 * existence by the serialized form.
     109                 */
     110                $serialized_block_name = strip_core_block_namespace( $block_name );
     111                if ( $serialized_block_name !== $block_name ) {
     112                        $has_block = false !== strpos( $post, '<!-- wp:' . $serialized_block_name . ' ' );
     113                }
     114        }
     115
     116        return $has_block;
    94117}
    95118
     
    112135
    113136        return $dynamic_block_names;
     137}
     138
     139/**
     140 * Given an array of attributes, returns a string in the serialized attributes
     141 * format prepared for post content.
     142 *
     143 * The serialized result is a JSON-encoded string, with unicode escape sequence
     144 * substitution for characters which might otherwise interfere with embedding
     145 * the result in an HTML comment.
     146 *
     147 * @since 5.3.1
     148 *
     149 * @param array $attributes Attributes object.
     150 * @return string Serialized attributes.
     151 */
     152function serialize_block_attributes( $block_attributes ) {
     153        $encoded_attributes = json_encode( $block_attributes );
     154        $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
     155        $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
     156        $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
     157        $encoded_attributes = preg_replace( '/&/', '\\u0026', $encoded_attributes );
     158        // Regex: /\\"/
     159        $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );
     160
     161        return $encoded_attributes;
     162}
     163
     164/**
     165 * Returns the block name to use for serialization. This will remove the default
     166 * "core/" namespace from a block name.
     167 *
     168 * @since 5.3.1
     169 *
     170 * @param string $block_name Original block name.
     171 * @return string Block name to use for serialization.
     172 */
     173function strip_core_block_namespace( $block_name = null ) {
     174        if ( is_string( $block_name ) && 0 === strpos( $block_name, 'core/' ) ) {
     175                return substr( $block_name, 5 );
     176        }
     177
     178        return $block_name;
     179}
     180
     181/**
     182 * Returns the content of a block, including comment delimiters.
     183 *
     184 * @since 5.3.1
     185 *
     186 * @param string $block_name Block name.
     187 * @param array  $attributes Block attributes.
     188 * @param string $content    Block save content.
     189 * @return string Comment-delimited block content.
     190 */
     191function get_comment_delimited_block_content( $block_name = null, $block_attributes, $block_content ) {
     192        if ( is_null( $block_name ) ) {
     193                return $block_content;
     194        }
     195
     196        $serialized_block_name = strip_core_block_namespace( $block_name );
     197        $serialized_attributes = empty( $block_attributes ) ? '' : serialize_block_attributes( $block_attributes ) . ' ';
     198
     199        if ( empty( $block_content ) ) {
     200                return sprintf( '<!-- wp:%s %s/-->', $serialized_block_name, $serialized_attributes );
     201        }
     202
     203        return sprintf(
     204                '<!-- wp:%s %s-->%s<!-- /wp:%s -->',
     205                $serialized_block_name,
     206                $serialized_attributes,
     207                $block_content,
     208                $serialized_block_name
     209        );
     210}
     211
     212/**
     213 * Returns the content of a block, including comment delimiters, serializing all
     214 * attributes from the given parsed block.
     215 *
     216 * This should be used when preparing a block to be saved to post content.
     217 * Prefer `render_block` when preparing a block for display. Unlike
     218 * `render_block`, this does not evaluate a block's `render_callback`, and will
     219 * instead preserve the markup as parsed.
     220 *
     221 * @since 5.3.1
     222 *
     223 * @param WP_Block_Parser_Block $block A single parsed block object.
     224 * @return string String of rendered HTML.
     225 */
     226function serialize_block( $block ) {
     227        $block_content = '';
     228
     229        $index = 0;
     230        foreach ( $block['innerContent'] as $chunk ) {
     231                $block_content .= is_string( $chunk ) ? $chunk : serialize_block( $block['innerBlocks'][ $index++ ] );
     232        }
     233
     234        if ( ! is_array( $block['attrs'] ) ) {
     235                $block['attrs'] = array();
     236        }
     237
     238        return get_comment_delimited_block_content(
     239                $block['blockName'],
     240                $block['attrs'],
     241                $block_content
     242        );
     243}
     244
     245/**
     246 * Returns a joined string of the aggregate serialization of the given parsed
     247 * blocks.
     248 *
     249 * @since 5.3.1
     250 *
     251 * @param WP_Block_Parser_Block[] $blocks Parsed block objects.
     252 * @return string String of rendered HTML.
     253 */
     254function serialize_blocks( $blocks ) {
     255        return implode( '', array_map( 'serialize_block', $blocks ) );
     256}
     257
     258/**
     259 * Filters and sanitizes block content to remove non-allowable HTML from
     260 * parsed block attribute values.
     261 *
     262 * @since 5.3.1
     263 *
     264 * @param string         $text              Text that may contain block content.
     265 * @param array[]|string $allowed_html      An array of allowed HTML elements
     266 *                                          and attributes, or a context name
     267 *                                          such as 'post'.
     268 * @param string[]       $allowed_protocols Array of allowed URL protocols.
     269 * @return string The filtered and sanitized content result.
     270 */
     271function filter_block_content( $text, $allowed_html = 'post', $allowed_protocols = array() ) {
     272        $result = '';
     273
     274        $blocks = parse_blocks( $text );
     275        foreach ( $blocks as $block ) {
     276                $block   = filter_block_kses( $block, $allowed_html, $allowed_protocols );
     277                $result .= serialize_block( $block );
     278        }
     279
     280        return $result;
     281}
     282
     283/**
     284 * Filters and sanitizes a parsed block to remove non-allowable HTML from block
     285 * attribute values.
     286 *
     287 * @since 5.3.1
     288 *
     289 * @param WP_Block_Parser_Block $block             The parsed block object.
     290 * @param array[]|string        $allowed_html      An array of allowed HTML
     291 *                                                 elements and attributes, or a
     292 *                                                 context name such as 'post'.
     293 * @param string[]              $allowed_protocols Allowed URL protocols.
     294 * @return array The filtered and sanitized block object result.
     295 */
     296function filter_block_kses( $block, $allowed_html, $allowed_protocols = array() ) {
     297        $block['attrs'] = filter_block_kses_value( $block['attrs'], $allowed_html, $allowed_protocols );
     298
     299        if ( is_array( $block['innerBlocks'] ) ) {
     300                foreach ( $block['innerBlocks'] as $i => $inner_block ) {
     301                        $block['innerBlocks'][ $i ] = filter_block_kses( $inner_block, $allowed_html, $allowed_protocols );
     302                }
     303        }
     304
     305        return $block;
     306}
     307
     308/**
     309 * Filters and sanitizes a parsed block attribute value to remove non-allowable
     310 * HTML.
     311 *
     312 * @since 5.3.1
     313 *
     314 * @param mixed          $value             The attribute value to filter.
     315 * @param array[]|string $allowed_html      An array of allowed HTML elements
     316 *                                          and attributes, or a context name
     317 *                                          such as 'post'.
     318 * @param string[]       $allowed_protocols Array of allowed URL protocols.
     319 * @return array The filtered and sanitized result.
     320 */
     321function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = array() ) {
     322        if ( is_array( $value ) ) {
     323                foreach ( $value as $key => $inner_value ) {
     324                        $filtered_key   = filter_block_kses_value( $key, $allowed_html, $allowed_protocols );
     325                        $filtered_value = filter_block_kses_value( $inner_value, $allowed_html, $allowed_protocols );
     326
     327                        if ( $filtered_key !== $key ) {
     328                                unset( $value[ $key ] );
     329                        }
     330
     331                        $value[ $filtered_key ] = $filtered_value;
     332                }
     333        } elseif ( is_string( $value ) ) {
     334                return wp_kses( $value, $allowed_html, $allowed_protocols );
     335        }
     336
     337        return $value;
    114338}
    115339
  • branches/5.1/src/wp-includes/default-filters.php

    r44714 r46907  
    246246add_filter( 'teeny_mce_before_init', '_mce_set_direction' );
    247247add_filter( 'pre_kses', 'wp_pre_kses_less_than' );
     248add_filter( 'pre_kses', 'wp_pre_kses_block_attributes', 10, 3 );
    248249add_filter( 'sanitize_title', 'sanitize_title_with_dashes', 10, 3 );
    249250add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
  • branches/5.1/src/wp-includes/formatting.php

    r45992 r46907  
    30423042function wp_targeted_link_rel( $text ) {
    30433043        // Don't run (more expensive) regex if no links with targets.
    3044         if ( stripos( $text, 'target' ) !== false && stripos( $text, '<a ' ) !== false ) {
    3045                 $text = preg_replace_callback( '|<a\s([^>]*target\s*=[^>]*)>|i', 'wp_targeted_link_rel_callback', $text );
     3044        if ( stripos( $text, 'target' ) === false || stripos( $text, '<a ' ) === false || is_serialized( $text ) ) {
     3045                return $text;
     3046        }
     3047
     3048        $script_and_style_regex = '/<(script|style).*?<\/\\1>/si';
     3049
     3050        preg_match_all( $script_and_style_regex, $text, $matches );
     3051        $extra_parts = $matches[0];
     3052        $html_parts  = preg_split( $script_and_style_regex, $text );
     3053
     3054        foreach ( $html_parts as &$part ) {
     3055                $part = preg_replace_callback( '|<a\s([^>]*target\s*=[^>]*)>|i', 'wp_targeted_link_rel_callback', $part );
     3056        }
     3057
     3058        $text = '';
     3059        for ( $i = 0; $i < count( $html_parts ); $i++ ) {
     3060                $text .= $html_parts[ $i ];
     3061                if ( isset( $extra_parts[ $i ] ) ) {
     3062                        $text .= $extra_parts[ $i ];
     3063                }
    30463064        }
    30473065
     
    30593077 */
    30603078function wp_targeted_link_rel_callback( $matches ) {
    3061         $link_html = $matches[1];
    3062         $rel_match = array();
     3079        $link_html          = $matches[1];
     3080        $original_link_html = $link_html;
     3081
     3082        // Consider the html escaped if there are no unescaped quotes
     3083        $is_escaped = ! preg_match( '/(^|[^\\\\])[\'"]/', $link_html );
     3084        if ( $is_escaped ) {
     3085                // Replace only the quotes so that they are parsable by wp_kses_hair, leave the rest as is
     3086                $link_html = preg_replace( '/\\\\([\'"])/', '$1', $link_html );
     3087        }
     3088
     3089        $atts = wp_kses_hair( $link_html, wp_allowed_protocols() );
    30633090
    30643091        /**
     
    30723099        $rel = apply_filters( 'wp_targeted_link_rel', 'noopener noreferrer', $link_html );
    30733100
    3074         // Avoid additional regex if the filter removes rel values.
    3075         if ( ! $rel ) {
    3076                 return "<a $link_html>";
    3077         }
    3078 
    3079         // Value with delimiters, spaces around are optional.
    3080         $attr_regex = '|rel\s*=\s*?(\\\\{0,1}["\'])(.*?)\\1|i';
    3081         preg_match( $attr_regex, $link_html, $rel_match );
    3082 
    3083         if ( empty( $rel_match[0] ) ) {
    3084                 // No delimiters, try with a single value and spaces, because `rel =  va"lue` is totally fine...
    3085                 $attr_regex = '|rel\s*=(\s*)([^\s]*)|i';
    3086                 preg_match( $attr_regex, $link_html, $rel_match );
    3087         }
    3088 
    3089         if ( ! empty( $rel_match[0] ) ) {
    3090                 $parts     = preg_split( '|\s+|', strtolower( $rel_match[2] ) );
    3091                 $parts     = array_map( 'esc_attr', $parts );
    3092                 $needed    = explode( ' ', $rel );
    3093                 $parts     = array_unique( array_merge( $parts, $needed ) );
    3094                 $delimiter = trim( $rel_match[1] ) ? $rel_match[1] : '"';
    3095                 $rel       = 'rel=' . $delimiter . trim( implode( ' ', $parts ) ) . $delimiter;
    3096                 $link_html = str_replace( $rel_match[0], $rel, $link_html );
    3097         } else {
    3098                 $link_html .= " rel=\"$rel\"";
     3101        // Return early if no rel values to be added or if no actual target attribute
     3102        if ( ! $rel || ! isset( $atts['target'] ) ) {
     3103                return "<a $original_link_html>";
     3104        }
     3105
     3106        if ( isset( $atts['rel'] ) ) {
     3107                $all_parts = preg_split( '/\s/', "{$atts['rel']['value']} $rel", -1, PREG_SPLIT_NO_EMPTY );
     3108                $rel       = implode( ' ', array_unique( $all_parts ) );
     3109        }
     3110
     3111        $atts['rel']['whole'] = 'rel="' . esc_attr( $rel ) . '"';
     3112        $link_html            = join( ' ', array_column( $atts, 'whole' ) );
     3113
     3114        if ( $is_escaped ) {
     3115                $link_html = preg_replace( '/[\'"]/', '\\\\$0', $link_html );
    30993116        }
    31003117
     
    47984815
    47994816/**
     4817 * Remove non-allowable HTML from parsed block attribute values when filtering
     4818 * in the post context.
     4819 *
     4820 * @since 5.3.1
     4821 *
     4822 * @param string         $string            Content to be run through KSES.
     4823 * @param array[]|string $allowed_html      An array of allowed HTML elements
     4824 *                                          and attributes, or a context name
     4825 *                                          such as 'post'.
     4826 * @param string[]       $allowed_protocols Array of allowed URL protocols.
     4827 * @return string Filtered text to run through KSES.
     4828 */
     4829function wp_pre_kses_block_attributes( $string, $allowed_html, $allowed_protocols ) {
     4830        /*
     4831         * `filter_block_content` is expected to call `wp_kses`. Temporarily remove
     4832         * the filter to avoid recursion.
     4833         */
     4834        remove_filter( 'pre_kses', 'wp_pre_kses_block_attributes', 10 );
     4835        $string = filter_block_content( $string, $allowed_html, $allowed_protocols );
     4836        add_filter( 'pre_kses', 'wp_pre_kses_block_attributes', 10, 3 );
     4837
     4838        return $string;
     4839}
     4840
     4841/**
    48004842 * WordPress implementation of PHP sprintf() with filters.
    48014843 *
  • branches/5.1/src/wp-includes/kses.php

    r46002 r46907  
    16591659function wp_kses_bad_protocol_once( $string, $allowed_protocols, $count = 1 ) {
    16601660        $string  = preg_replace( '/(&#0*58(?![;0-9])|&#x0*3a(?![;a-f0-9]))/i', '$1;', $string );
    1661         $string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
     1661        $string2 = preg_split( '/:|&#0*58;|&#x0*3a;|&colon;/i', $string, 2 );
    16621662        if ( isset( $string2[1] ) && ! preg_match( '%/\?%', $string2[0] ) ) {
    16631663                $string   = trim( $string2[1] );
  • branches/5.1/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    r44225 r46907  
    500500                }
    501501
    502                 if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
     502                if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
    503503                        return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
    504504                }
     
    655655                }
    656656
    657                 if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) ) {
     657                if ( ! empty( $request['sticky'] ) && ! current_user_can( $post_type->cap->edit_others_posts ) && ! current_user_can( $post_type->cap->publish_posts ) ) {
    658658                        return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) );
    659659                }
     
    957957         */
    958958        protected function prepare_item_for_database( $request ) {
    959                 $prepared_post = new stdClass;
     959                $prepared_post = new stdClass();
    960960
    961961                // Post ID.
  • branches/5.1/tests/phpunit/tests/blocks/block-type.php

    r44269 r46907  
    303303                // even if it detects a proper $post global it should still be false for a missing block.
    304304                $this->assertFalse( has_block( 'core/fake' ) );
     305        }
     306
     307        public function test_post_has_block_serialized_name() {
     308                $content = '<!-- wp:serialized /--><!-- wp:core/normalized /--><!-- wp:plugin/third-party /-->';
     309
     310                $this->assertTrue( has_block( 'core/serialized', $content ) );
     311
     312                /*
     313                 * Technically, `has_block` should receive a "full" (normalized, parsed)
     314                 * block name. But this test conforms to expected pre-5.3.1 behavior.
     315                 */
     316                $this->assertTrue( has_block( 'serialized', $content ) );
     317                $this->assertTrue( has_block( 'core/normalized', $content ) );
     318                $this->assertTrue( has_block( 'normalized', $content ) );
     319                $this->assertFalse( has_block( 'plugin/normalized', $content ) );
     320                $this->assertFalse( has_block( 'plugin/serialized', $content ) );
     321                $this->assertFalse( has_block( 'third-party', $content ) );
     322                $this->assertFalse( has_block( 'core/third-party', $content ) );
    305323        }
    306324
  • branches/5.1/tests/phpunit/tests/formatting/WPTargetedLinkRel.php

    r44714 r46907  
    3939        public function test_rel_with_single_quote_delimiter() {
    4040                $content  = '<p>Links: <a href="/" rel=\'existing values\' target="_blank">Existing rel</a></p>';
    41                 $expected = '<p>Links: <a href="/" rel=\'existing values noopener noreferrer\' target="_blank">Existing rel</a></p>';
     41                $expected = '<p>Links: <a href="/" rel="existing values noopener noreferrer" target="_blank">Existing rel</a></p>';
    4242                $this->assertEquals( $expected, wp_targeted_link_rel( $content ) );
    4343        }
     
    5252                $content  = '<p>Links: <a href="/" rel = existing target="_blank">Existing rel</a></p>';
    5353                $expected = '<p>Links: <a href="/" rel="existing noopener noreferrer" target="_blank">Existing rel</a></p>';
    54                 $this->assertEquals( $expected, wp_targeted_link_rel( $content ) );
    55         }
    56 
    57         public function test_rel_value_spaced_and_no_delimiter_and_values_to_escape() {
    58                 $content  = '<p>Links: <a href="/" rel = existing"value target="_blank">Existing rel</a></p>';
    59                 $expected = '<p>Links: <a href="/" rel="existing&quot;value noopener noreferrer" target="_blank">Existing rel</a></p>';
    6054                $this->assertEquals( $expected, wp_targeted_link_rel( $content ) );
    6155        }
     
    10296                $this->assertEquals( $expected, $post->post_content );
    10397        }
     98
     99        /**
     100         * Ensure JSON format is preserved when relation attribute (rel) is missing.
     101         *
     102         * @ticket 46316
     103         */
     104        public function test_wp_targeted_link_rel_should_preserve_json() {
     105                $content  = '<p>Links: <a href=\"\/\" target=\"_blank\">No rel<\/a><\/p>';
     106                $expected = '<p>Links: <a href=\"\/\" target=\"_blank\" rel=\"noopener noreferrer\">No rel<\/a><\/p>';
     107                $this->assertEquals( $expected, wp_targeted_link_rel( $content ) );
     108        }
     109
     110        /**
     111         * Ensure the content of style and script tags are not processed
     112         *
     113         * @ticket 47244
     114         */
     115        public function test_wp_targeted_link_rel_skips_style_and_scripts() {
     116                $content  = '<style><a href="/" target=a></style><p>Links: <script>console.log("<a href=\'/\' target=a>hi</a>");</script><script>alert(1);</script>here <a href="/" target=_blank>aq</a></p><script>console.log("<a href=\'last\' target=\'_blank\'")</script>';
     117                $expected = '<style><a href="/" target=a></style><p>Links: <script>console.log("<a href=\'/\' target=a>hi</a>");</script><script>alert(1);</script>here <a href="/" target="_blank" rel="noopener noreferrer">aq</a></p><script>console.log("<a href=\'last\' target=\'_blank\'")</script>';
     118                $this->assertEquals( $expected, wp_targeted_link_rel( $content ) );
     119        }
     120
     121        /**
     122         * Ensure entirely serialized content is ignored.
     123         *
     124         * @ticket 46402
     125         */
     126        public function test_ignore_entirely_serialized_content() {
     127                $content  = 'a:1:{s:4:"html";s:52:"<p>Links: <a href="/" target="_blank">No Rel</a></p>";}';
     128                $expected = 'a:1:{s:4:"html";s:52:"<p>Links: <a href="/" target="_blank">No Rel</a></p>";}';
     129                $this->assertEquals( $expected, wp_targeted_link_rel( $content ) );
     130        }
     131
     132        public function test_wp_targeted_link_rel_tab_separated_values_are_split() {
     133                $content  = "<p>Links: <a href=\"/\" target=\"_blank\" rel=\"ugc\t\tnoopener\t\">No rel</a></p>";
     134                $expected = '<p>Links: <a href="/" target="_blank" rel="ugc noopener noreferrer">No rel</a></p>';
     135                $this->assertEquals( $expected, wp_targeted_link_rel( $content ) );
     136        }
     137
    104138}
  • branches/5.1/tests/phpunit/tests/kses.php

    r46002 r46907  
    179179                }
    180180
     181                $bad_not_normalized = array(
     182                        'dummy&colon;alert(1)',
     183                        'javascript&colon;alert(1)',
     184                        'javascript&CoLon;alert(1)',
     185                        'javascript&COLON;alert(1);',
     186                        'javascript&#58;alert(1);',
     187                        'javascript&#0058;alert(1);',
     188                        'javascript&#0000058alert(1);',
     189                        'jav    ascript&COLON;alert(1);',
     190                        'javascript&#58;javascript&colon;alert(1);',
     191                        'javascript&#58;javascript&colon;alert(1);',
     192                        'javascript&#0000058javascript&colon;alert(1);',
     193                        'javascript&#58;javascript&#0000058alert(1);',
     194                        'javascript&#58alert(1)',
     195                );
     196                foreach ( $bad_not_normalized as $k => $x ) {
     197                        $result = wp_kses_bad_protocol( $x, wp_allowed_protocols() );
     198                        if ( ! empty( $result ) && 'alert(1);' !== $result && 'alert(1)' !== $result ) {
     199                                $this->fail( "wp_kses_bad_protocol failed on $k, $x. Result: $result" );
     200                        }
     201                }
     202
    181203                $safe = array(
    182204                        'dummy:alert(1)',
Note: See TracChangeset for help on using the changeset viewer.