[wp-trac] [WordPress Trac] #19414: Filter 'kses_allowed_protocols' is only applied once in function wp_allowed_protocols() & function esc_url() returns empty string;

WordPress Trac wp-trac at lists.automattic.com
Fri Dec 2 14:29:51 UTC 2011


#19414: Filter 'kses_allowed_protocols' is only applied once in function
wp_allowed_protocols()  & function esc_url() returns empty string;
--------------------------+-------------------------------
 Reporter:  Anatta        |      Owner:
     Type:  defect (bug)  |     Status:  new
 Priority:  normal        |  Milestone:  Awaiting Review
Component:  Security      |    Version:  3.3
 Severity:  major         |   Keywords:  reporter-feedback
--------------------------+-------------------------------
 '''Files:''' wp-includes/functions.php: ''Lines:4665-4674'' and wp-
 includes/formatting.php ''Lines: 2339 to 2342''.

 ''apply_filters( 'kses_allowed_protocols', $protocols );'' is only called
 when ''wp_allowed_protocols()'' is first initiated.  This makes it
 impossible to grant a temporary protocol exception.

 I discovered this in a plugin trying to add a javascript link to the admin
 bar.  Function ''esc_url()'' (wp-includes/formatting.php) would normally
 return blank for ''javascript:myFunction(/* code */);'', however, I want
 to temporarily allow the javascript protocol between the
 ''wp_before_admin_bar_render'' and ''wp_after_admin_bar_render actions''
 by applying a filter.

 This is currently impossible because the other possible filter,
 ''clean_url'' is not fired in ''esc_url'' if ''wp_kses_bad_protocol()''
 returns false.  The only current option to allow javascript as a protocol
 on the admin bar is to add a permanent filter - not great for security
 should it become known that plugins with working javascript on the admin
 bar (e.g. a Facebook Connect logout function) must have the javascript
 protocol allowed site-wide.

 ''The current wp_allowed_protocols() in functions.php:''
 {{{
 function wp_allowed_protocols() {
         static $protocols;

         if ( empty( $protocols ) ) {
                 $protocols = array( 'http', 'https', 'ftp', 'ftps',
 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms',
 'rtsp', 'svn' );
                 $protocols = apply_filters( 'kses_allowed_protocols',
 $protocols );
         }

         return $protocols;
 }
 }}}

 '''Fix suggestion 1''': Move the apply_filter outside the ''if ( empty(
 $protocols ) )'' statement:

 ''Patched wp_allowed_protocols()''
 {{{
 function wp_allowed_protocols() {
         static $protocols;

         if ( empty( $protocols ) ) {
                 $protocols = array( 'http', 'https', 'ftp', 'ftps',
 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms',
 'rtsp', 'svn' );
         }

         $protocols = apply_filters( 'kses_allowed_protocols', $protocols
 );

         return $protocols;
 }
 }}}

 '''Fix suggestion 2''': Change ''esc_url()'' in wp-includes/formatting.php
 not to ''return'' directly, but to set ''$url'' to empty instead of
 directly returning an empty string, allowing ''$url'' to pass through the
 ''clean_url'' filter.

 ''Patched esc_url()'' (Changed line 2342 from ''return'' to ''$url ='')
 {{{
 function esc_url( $url, $protocols = null, $_context = 'display' ) {
         $original_url = $url;

         if ( '' == $url )
                 return $url;
         $url =
 preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
         $strip = array('%0d', '%0a', '%0D', '%0A');
         $url = _deep_replace($strip, $url);
         $url = str_replace(';//', '://', $url);
         /* If the URL doesn't appear to contain a scheme, we
          * presume it needs http:// appended (unless a relative
          * link starting with /, # or ? or a php file).
          */
         if ( strpos($url, ':') === false && ! in_array( $url[0], array(
 '/', '#', '?' ) ) &&
                 ! preg_match('/^[a-z0-9-]+?\.php/i', $url) )
                 $url = 'http://' . $url;

         // Replace ampersands and single quotes only when displaying.
         if ( 'display' == $_context ) {
                 $url = wp_kses_normalize_entities( $url );
                 $url = str_replace( '&', '&', $url );
                 $url = str_replace( "'", ''', $url );
         }

         if ( ! is_array( $protocols ) )
                 $protocols = wp_allowed_protocols();
         if ( wp_kses_bad_protocol( $url, $protocols ) != $url )
                 $url = '';

         return apply_filters('clean_url', $url, $original_url, $_context);
 }
 }}}

 One of both of these minor patches before 3.3 final would be greatly
 appreciated.

-- 
Ticket URL: <http://core.trac.wordpress.org/ticket/19414>
WordPress Trac <http://core.trac.wordpress.org/>
WordPress blogging software


More information about the wp-trac mailing list