wp/wp-includes/kses.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    26  * @copyright (C) 2002, 2003, 2005
    26  * @copyright (C) 2002, 2003, 2005
    27  * @author Ulf Harnhammar <http://advogato.org/person/metaur/>
    27  * @author Ulf Harnhammar <http://advogato.org/person/metaur/>
    28  *
    28  *
    29  * @package External
    29  * @package External
    30  * @subpackage KSES
    30  * @subpackage KSES
    31  *
    31  */
    32  */
    32 
    33 
    33 /**
    34 /**
    34  * Specifies the default allowable HTML tags.
    35  * You can override this in a plugin.
    35  *
    36  *
    36  * Using `CUSTOM_TAGS` is not recommended and should be considered deprecated. The
    37  * The {@see 'wp_kses_allowed_html'} filter is more powerful and supplies context.
    37  * {@see 'wp_kses_allowed_html'} filter is more powerful and supplies context.
    38  *
       
    39  * `CUSTOM_TAGS` is not recommended and should be considered deprecated.
       
    40  *
    38  *
    41  * @see wp_kses_allowed_html()
    39  * @see wp_kses_allowed_html()
    42  *
       
    43  * @since 1.2.0
    40  * @since 1.2.0
    44  */
    41  *
    45 if ( ! defined( 'CUSTOM_TAGS' ) )
    42  * @var array[]|bool Array of default allowable HTML tags, or false to use the defaults.
       
    43  */
       
    44 if ( ! defined( 'CUSTOM_TAGS' ) ) {
    46 	define( 'CUSTOM_TAGS', false );
    45 	define( 'CUSTOM_TAGS', false );
       
    46 }
    47 
    47 
    48 // Ensure that these variables are added to the global namespace
    48 // Ensure that these variables are added to the global namespace
    49 // (e.g. if using namespaces / autoload in the current PHP environment).
    49 // (e.g. if using namespaces / autoload in the current PHP environment).
    50 global $allowedposttags, $allowedtags, $allowedentitynames;
    50 global $allowedposttags, $allowedtags, $allowedentitynames;
    51 
    51 
    52 if ( ! CUSTOM_TAGS ) {
    52 if ( ! CUSTOM_TAGS ) {
    53 	/**
    53 	/**
    54 	 * Kses global for default allowable HTML tags.
    54 	 * KSES global for default allowable HTML tags.
    55 	 *
    55 	 *
    56 	 * Can be override by using CUSTOM_TAGS constant.
    56 	 * Can be overridden with the `CUSTOM_TAGS` constant.
    57 	 *
    57 	 *
    58 	 * @global array $allowedposttags
    58 	 * @var array[] $allowedposttags Array of default allowable HTML tags.
    59 	 * @since 2.0.0
    59 	 * @since 2.0.0
    60 	 */
    60 	 */
    61 	$allowedposttags = array(
    61 	$allowedposttags = array(
    62 		'address' => array(),
    62 		'address'    => array(),
    63 		'a' => array(
    63 		'a'          => array(
    64 			'href' => true,
    64 			'href'     => true,
    65 			'rel' => true,
    65 			'rel'      => true,
    66 			'rev' => true,
    66 			'rev'      => true,
    67 			'name' => true,
    67 			'name'     => true,
       
    68 			'target'   => true,
       
    69 			'download' => array(
       
    70 				'valueless' => 'y',
       
    71 			),
       
    72 		),
       
    73 		'abbr'       => array(),
       
    74 		'acronym'    => array(),
       
    75 		'area'       => array(
       
    76 			'alt'    => true,
       
    77 			'coords' => true,
       
    78 			'href'   => true,
       
    79 			'nohref' => true,
       
    80 			'shape'  => true,
    68 			'target' => true,
    81 			'target' => true,
    69 		),
    82 		),
    70 		'abbr' => array(),
    83 		'article'    => array(
    71 		'acronym' => array(),
    84 			'align'    => true,
    72 		'area' => array(
    85 			'dir'      => true,
    73 			'alt' => true,
    86 			'lang'     => true,
    74 			'coords' => true,
       
    75 			'href' => true,
       
    76 			'nohref' => true,
       
    77 			'shape' => true,
       
    78 			'target' => true,
       
    79 		),
       
    80 		'article' => array(
       
    81 			'align' => true,
       
    82 			'dir' => true,
       
    83 			'lang' => true,
       
    84 			'xml:lang' => true,
    87 			'xml:lang' => true,
    85 		),
    88 		),
    86 		'aside' => array(
    89 		'aside'      => array(
    87 			'align' => true,
    90 			'align'    => true,
    88 			'dir' => true,
    91 			'dir'      => true,
    89 			'lang' => true,
    92 			'lang'     => true,
    90 			'xml:lang' => true,
    93 			'xml:lang' => true,
    91 		),
    94 		),
    92 		'audio' => array(
    95 		'audio'      => array(
    93 			'autoplay' => true,
    96 			'autoplay' => true,
    94 			'controls' => true,
    97 			'controls' => true,
    95 			'loop' => true,
    98 			'loop'     => true,
    96 			'muted' => true,
    99 			'muted'    => true,
    97 			'preload' => true,
   100 			'preload'  => true,
    98 			'src' => true,
   101 			'src'      => true,
    99 		),
   102 		),
   100 		'b' => array(),
   103 		'b'          => array(),
   101 		'bdo' => array(
   104 		'bdo'        => array(
   102 			'dir' => true,
   105 			'dir' => true,
   103 		),
   106 		),
   104 		'big' => array(),
   107 		'big'        => array(),
   105 		'blockquote' => array(
   108 		'blockquote' => array(
   106 			'cite' => true,
   109 			'cite'     => true,
       
   110 			'lang'     => true,
       
   111 			'xml:lang' => true,
       
   112 		),
       
   113 		'br'         => array(),
       
   114 		'button'     => array(
       
   115 			'disabled' => true,
       
   116 			'name'     => true,
       
   117 			'type'     => true,
       
   118 			'value'    => true,
       
   119 		),
       
   120 		'caption'    => array(
       
   121 			'align' => true,
       
   122 		),
       
   123 		'cite'       => array(
       
   124 			'dir'  => true,
   107 			'lang' => true,
   125 			'lang' => true,
       
   126 		),
       
   127 		'code'       => array(),
       
   128 		'col'        => array(
       
   129 			'align'   => true,
       
   130 			'char'    => true,
       
   131 			'charoff' => true,
       
   132 			'span'    => true,
       
   133 			'dir'     => true,
       
   134 			'valign'  => true,
       
   135 			'width'   => true,
       
   136 		),
       
   137 		'colgroup'   => array(
       
   138 			'align'   => true,
       
   139 			'char'    => true,
       
   140 			'charoff' => true,
       
   141 			'span'    => true,
       
   142 			'valign'  => true,
       
   143 			'width'   => true,
       
   144 		),
       
   145 		'del'        => array(
       
   146 			'datetime' => true,
       
   147 		),
       
   148 		'dd'         => array(),
       
   149 		'dfn'        => array(),
       
   150 		'details'    => array(
       
   151 			'align'    => true,
       
   152 			'dir'      => true,
       
   153 			'lang'     => true,
       
   154 			'open'     => true,
   108 			'xml:lang' => true,
   155 			'xml:lang' => true,
   109 		),
   156 		),
   110 		'br' => array(),
   157 		'div'        => array(
   111 		'button' => array(
   158 			'align'    => true,
   112 			'disabled' => true,
   159 			'dir'      => true,
   113 			'name' => true,
   160 			'lang'     => true,
   114 			'type' => true,
   161 			'xml:lang' => true,
   115 			'value' => true,
   162 		),
   116 		),
   163 		'dl'         => array(),
   117 		'caption' => array(
   164 		'dt'         => array(),
       
   165 		'em'         => array(),
       
   166 		'fieldset'   => array(),
       
   167 		'figure'     => array(
       
   168 			'align'    => true,
       
   169 			'dir'      => true,
       
   170 			'lang'     => true,
       
   171 			'xml:lang' => true,
       
   172 		),
       
   173 		'figcaption' => array(
       
   174 			'align'    => true,
       
   175 			'dir'      => true,
       
   176 			'lang'     => true,
       
   177 			'xml:lang' => true,
       
   178 		),
       
   179 		'font'       => array(
       
   180 			'color' => true,
       
   181 			'face'  => true,
       
   182 			'size'  => true,
       
   183 		),
       
   184 		'footer'     => array(
       
   185 			'align'    => true,
       
   186 			'dir'      => true,
       
   187 			'lang'     => true,
       
   188 			'xml:lang' => true,
       
   189 		),
       
   190 		'h1'         => array(
   118 			'align' => true,
   191 			'align' => true,
   119 		),
   192 		),
   120 		'cite' => array(
   193 		'h2'         => array(
   121 			'dir' => true,
       
   122 			'lang' => true,
       
   123 		),
       
   124 		'code' => array(),
       
   125 		'col' => array(
       
   126 			'align' => true,
   194 			'align' => true,
   127 			'char' => true,
   195 		),
   128 			'charoff' => true,
   196 		'h3'         => array(
   129 			'span' => true,
       
   130 			'dir' => true,
       
   131 			'valign' => true,
       
   132 			'width' => true,
       
   133 		),
       
   134 		'colgroup' => array(
       
   135 			'align' => true,
   197 			'align' => true,
   136 			'char' => true,
   198 		),
   137 			'charoff' => true,
   199 		'h4'         => array(
   138 			'span' => true,
   200 			'align' => true,
   139 			'valign' => true,
   201 		),
   140 			'width' => true,
   202 		'h5'         => array(
   141 		),
   203 			'align' => true,
   142 		'del' => array(
   204 		),
       
   205 		'h6'         => array(
       
   206 			'align' => true,
       
   207 		),
       
   208 		'header'     => array(
       
   209 			'align'    => true,
       
   210 			'dir'      => true,
       
   211 			'lang'     => true,
       
   212 			'xml:lang' => true,
       
   213 		),
       
   214 		'hgroup'     => array(
       
   215 			'align'    => true,
       
   216 			'dir'      => true,
       
   217 			'lang'     => true,
       
   218 			'xml:lang' => true,
       
   219 		),
       
   220 		'hr'         => array(
       
   221 			'align'   => true,
       
   222 			'noshade' => true,
       
   223 			'size'    => true,
       
   224 			'width'   => true,
       
   225 		),
       
   226 		'i'          => array(),
       
   227 		'img'        => array(
       
   228 			'alt'      => true,
       
   229 			'align'    => true,
       
   230 			'border'   => true,
       
   231 			'height'   => true,
       
   232 			'hspace'   => true,
       
   233 			'longdesc' => true,
       
   234 			'vspace'   => true,
       
   235 			'src'      => true,
       
   236 			'usemap'   => true,
       
   237 			'width'    => true,
       
   238 		),
       
   239 		'ins'        => array(
   143 			'datetime' => true,
   240 			'datetime' => true,
   144 		),
   241 			'cite'     => true,
   145 		'dd' => array(),
   242 		),
   146 		'dfn' => array(),
   243 		'kbd'        => array(),
   147 		'details' => array(
   244 		'label'      => array(
       
   245 			'for' => true,
       
   246 		),
       
   247 		'legend'     => array(
   148 			'align' => true,
   248 			'align' => true,
   149 			'dir' => true,
   249 		),
   150 			'lang' => true,
   250 		'li'         => array(
   151 			'open' => true,
       
   152 			'xml:lang' => true,
       
   153 		),
       
   154 		'div' => array(
       
   155 			'align' => true,
       
   156 			'dir' => true,
       
   157 			'lang' => true,
       
   158 			'xml:lang' => true,
       
   159 		),
       
   160 		'dl' => array(),
       
   161 		'dt' => array(),
       
   162 		'em' => array(),
       
   163 		'fieldset' => array(),
       
   164 		'figure' => array(
       
   165 			'align' => true,
       
   166 			'dir' => true,
       
   167 			'lang' => true,
       
   168 			'xml:lang' => true,
       
   169 		),
       
   170 		'figcaption' => array(
       
   171 			'align' => true,
       
   172 			'dir' => true,
       
   173 			'lang' => true,
       
   174 			'xml:lang' => true,
       
   175 		),
       
   176 		'font' => array(
       
   177 			'color' => true,
       
   178 			'face' => true,
       
   179 			'size' => true,
       
   180 		),
       
   181 		'footer' => array(
       
   182 			'align' => true,
       
   183 			'dir' => true,
       
   184 			'lang' => true,
       
   185 			'xml:lang' => true,
       
   186 		),
       
   187 		'form' => array(
       
   188 			'action' => true,
       
   189 			'accept' => true,
       
   190 			'accept-charset' => true,
       
   191 			'enctype' => true,
       
   192 			'method' => true,
       
   193 			'name' => true,
       
   194 			'target' => true,
       
   195 		),
       
   196 		'h1' => array(
       
   197 			'align' => true,
       
   198 		),
       
   199 		'h2' => array(
       
   200 			'align' => true,
       
   201 		),
       
   202 		'h3' => array(
       
   203 			'align' => true,
       
   204 		),
       
   205 		'h4' => array(
       
   206 			'align' => true,
       
   207 		),
       
   208 		'h5' => array(
       
   209 			'align' => true,
       
   210 		),
       
   211 		'h6' => array(
       
   212 			'align' => true,
       
   213 		),
       
   214 		'header' => array(
       
   215 			'align' => true,
       
   216 			'dir' => true,
       
   217 			'lang' => true,
       
   218 			'xml:lang' => true,
       
   219 		),
       
   220 		'hgroup' => array(
       
   221 			'align' => true,
       
   222 			'dir' => true,
       
   223 			'lang' => true,
       
   224 			'xml:lang' => true,
       
   225 		),
       
   226 		'hr' => array(
       
   227 			'align' => true,
       
   228 			'noshade' => true,
       
   229 			'size' => true,
       
   230 			'width' => true,
       
   231 		),
       
   232 		'i' => array(),
       
   233 		'img' => array(
       
   234 			'alt' => true,
       
   235 			'align' => true,
       
   236 			'border' => true,
       
   237 			'height' => true,
       
   238 			'hspace' => true,
       
   239 			'longdesc' => true,
       
   240 			'vspace' => true,
       
   241 			'src' => true,
       
   242 			'usemap' => true,
       
   243 			'width' => true,
       
   244 		),
       
   245 		'ins' => array(
       
   246 			'datetime' => true,
       
   247 			'cite' => true,
       
   248 		),
       
   249 		'kbd' => array(),
       
   250 		'label' => array(
       
   251 			'for' => true,
       
   252 		),
       
   253 		'legend' => array(
       
   254 			'align' => true,
       
   255 		),
       
   256 		'li' => array(
       
   257 			'align' => true,
   251 			'align' => true,
   258 			'value' => true,
   252 			'value' => true,
   259 		),
   253 		),
   260 		'map' => array(
   254 		'map'        => array(
   261 			'name' => true,
   255 			'name' => true,
   262 		),
   256 		),
   263 		'mark' => array(),
   257 		'mark'       => array(),
   264 		'menu' => array(
   258 		'menu'       => array(
   265 			'type' => true,
   259 			'type' => true,
   266 		),
   260 		),
   267 		'nav' => array(
   261 		'nav'        => array(
   268 			'align' => true,
   262 			'align'    => true,
   269 			'dir' => true,
   263 			'dir'      => true,
   270 			'lang' => true,
   264 			'lang'     => true,
   271 			'xml:lang' => true,
   265 			'xml:lang' => true,
   272 		),
   266 		),
   273 		'p' => array(
   267 		'p'          => array(
   274 			'align' => true,
   268 			'align'    => true,
   275 			'dir' => true,
   269 			'dir'      => true,
   276 			'lang' => true,
   270 			'lang'     => true,
   277 			'xml:lang' => true,
   271 			'xml:lang' => true,
   278 		),
   272 		),
   279 		'pre' => array(
   273 		'pre'        => array(
   280 			'width' => true,
   274 			'width' => true,
   281 		),
   275 		),
   282 		'q' => array(
   276 		'q'          => array(
   283 			'cite' => true,
   277 			'cite' => true,
   284 		),
   278 		),
   285 		's' => array(),
   279 		's'          => array(),
   286 		'samp' => array(),
   280 		'samp'       => array(),
   287 		'span' => array(
   281 		'span'       => array(
   288 			'dir' => true,
   282 			'dir'      => true,
   289 			'align' => true,
   283 			'align'    => true,
   290 			'lang' => true,
   284 			'lang'     => true,
   291 			'xml:lang' => true,
   285 			'xml:lang' => true,
   292 		),
   286 		),
   293 		'section' => array(
   287 		'section'    => array(
   294 			'align' => true,
   288 			'align'    => true,
   295 			'dir' => true,
   289 			'dir'      => true,
   296 			'lang' => true,
   290 			'lang'     => true,
   297 			'xml:lang' => true,
   291 			'xml:lang' => true,
   298 		),
   292 		),
   299 		'small' => array(),
   293 		'small'      => array(),
   300 		'strike' => array(),
   294 		'strike'     => array(),
   301 		'strong' => array(),
   295 		'strong'     => array(),
   302 		'sub' => array(),
   296 		'sub'        => array(),
   303 		'summary' => array(
   297 		'summary'    => array(
   304 			'align' => true,
   298 			'align'    => true,
   305 			'dir' => true,
   299 			'dir'      => true,
   306 			'lang' => true,
   300 			'lang'     => true,
   307 			'xml:lang' => true,
   301 			'xml:lang' => true,
   308 		),
   302 		),
   309 		'sup' => array(),
   303 		'sup'        => array(),
   310 		'table' => array(
   304 		'table'      => array(
   311 			'align' => true,
   305 			'align'       => true,
   312 			'bgcolor' => true,
   306 			'bgcolor'     => true,
   313 			'border' => true,
   307 			'border'      => true,
   314 			'cellpadding' => true,
   308 			'cellpadding' => true,
   315 			'cellspacing' => true,
   309 			'cellspacing' => true,
   316 			'dir' => true,
   310 			'dir'         => true,
   317 			'rules' => true,
   311 			'rules'       => true,
   318 			'summary' => true,
   312 			'summary'     => true,
   319 			'width' => true,
   313 			'width'       => true,
   320 		),
   314 		),
   321 		'tbody' => array(
   315 		'tbody'      => array(
   322 			'align' => true,
   316 			'align'   => true,
   323 			'char' => true,
   317 			'char'    => true,
   324 			'charoff' => true,
   318 			'charoff' => true,
   325 			'valign' => true,
   319 			'valign'  => true,
   326 		),
   320 		),
   327 		'td' => array(
   321 		'td'         => array(
   328 			'abbr' => true,
   322 			'abbr'    => true,
   329 			'align' => true,
   323 			'align'   => true,
   330 			'axis' => true,
   324 			'axis'    => true,
   331 			'bgcolor' => true,
   325 			'bgcolor' => true,
   332 			'char' => true,
   326 			'char'    => true,
   333 			'charoff' => true,
   327 			'charoff' => true,
   334 			'colspan' => true,
   328 			'colspan' => true,
   335 			'dir' => true,
   329 			'dir'     => true,
   336 			'headers' => true,
   330 			'headers' => true,
   337 			'height' => true,
   331 			'height'  => true,
   338 			'nowrap' => true,
   332 			'nowrap'  => true,
   339 			'rowspan' => true,
   333 			'rowspan' => true,
   340 			'scope' => true,
   334 			'scope'   => true,
   341 			'valign' => true,
   335 			'valign'  => true,
   342 			'width' => true,
   336 			'width'   => true,
   343 		),
   337 		),
   344 		'textarea' => array(
   338 		'textarea'   => array(
   345 			'cols' => true,
   339 			'cols'     => true,
   346 			'rows' => true,
   340 			'rows'     => true,
   347 			'disabled' => true,
   341 			'disabled' => true,
   348 			'name' => true,
   342 			'name'     => true,
   349 			'readonly' => true,
   343 			'readonly' => true,
   350 		),
   344 		),
   351 		'tfoot' => array(
   345 		'tfoot'      => array(
   352 			'align' => true,
   346 			'align'   => true,
   353 			'char' => true,
   347 			'char'    => true,
   354 			'charoff' => true,
   348 			'charoff' => true,
   355 			'valign' => true,
   349 			'valign'  => true,
   356 		),
   350 		),
   357 		'th' => array(
   351 		'th'         => array(
   358 			'abbr' => true,
   352 			'abbr'    => true,
   359 			'align' => true,
   353 			'align'   => true,
   360 			'axis' => true,
   354 			'axis'    => true,
   361 			'bgcolor' => true,
   355 			'bgcolor' => true,
   362 			'char' => true,
   356 			'char'    => true,
   363 			'charoff' => true,
   357 			'charoff' => true,
   364 			'colspan' => true,
   358 			'colspan' => true,
   365 			'headers' => true,
   359 			'headers' => true,
   366 			'height' => true,
   360 			'height'  => true,
   367 			'nowrap' => true,
   361 			'nowrap'  => true,
   368 			'rowspan' => true,
   362 			'rowspan' => true,
   369 			'scope' => true,
   363 			'scope'   => true,
   370 			'valign' => true,
   364 			'valign'  => true,
   371 			'width' => true,
   365 			'width'   => true,
   372 		),
   366 		),
   373 		'thead' => array(
   367 		'thead'      => array(
   374 			'align' => true,
   368 			'align'   => true,
   375 			'char' => true,
   369 			'char'    => true,
   376 			'charoff' => true,
   370 			'charoff' => true,
   377 			'valign' => true,
   371 			'valign'  => true,
   378 		),
   372 		),
   379 		'title' => array(),
   373 		'title'      => array(),
   380 		'tr' => array(
   374 		'tr'         => array(
   381 			'align' => true,
   375 			'align'   => true,
   382 			'bgcolor' => true,
   376 			'bgcolor' => true,
   383 			'char' => true,
   377 			'char'    => true,
   384 			'charoff' => true,
   378 			'charoff' => true,
   385 			'valign' => true,
   379 			'valign'  => true,
   386 		),
   380 		),
   387 		'track' => array(
   381 		'track'      => array(
   388 			'default' => true,
   382 			'default' => true,
   389 			'kind' => true,
   383 			'kind'    => true,
   390 			'label' => true,
   384 			'label'   => true,
   391 			'src' => true,
   385 			'src'     => true,
   392 			'srclang' => true,
   386 			'srclang' => true,
   393 		),
   387 		),
   394 		'tt' => array(),
   388 		'tt'         => array(),
   395 		'u' => array(),
   389 		'u'          => array(),
   396 		'ul' => array(
   390 		'ul'         => array(
   397 			'type' => true,
   391 			'type' => true,
   398 		),
   392 		),
   399 		'ol' => array(
   393 		'ol'         => array(
   400 			'start' => true,
   394 			'start'    => true,
   401 			'type' => true,
   395 			'type'     => true,
   402 			'reversed' => true,
   396 			'reversed' => true,
   403 		),
   397 		),
   404 		'var' => array(),
   398 		'var'        => array(),
   405 		'video' => array(
   399 		'video'      => array(
   406 			'autoplay' => true,
   400 			'autoplay' => true,
   407 			'controls' => true,
   401 			'controls' => true,
   408 			'height' => true,
   402 			'height'   => true,
   409 			'loop' => true,
   403 			'loop'     => true,
   410 			'muted' => true,
   404 			'muted'    => true,
   411 			'poster' => true,
   405 			'poster'   => true,
   412 			'preload' => true,
   406 			'preload'  => true,
   413 			'src' => true,
   407 			'src'      => true,
   414 			'width' => true,
   408 			'width'    => true,
   415 		),
   409 		),
   416 	);
   410 	);
   417 
   411 
   418 	/**
   412 	/**
   419 	 * Kses allowed HTML elements.
   413 	 * @var array[] $allowedtags Array of KSES allowed HTML elements.
   420 	 *
       
   421 	 * @global array $allowedtags
       
   422 	 * @since 1.0.0
   414 	 * @since 1.0.0
   423 	 */
   415 	 */
   424 	$allowedtags = array(
   416 	$allowedtags = array(
   425 		'a' => array(
   417 		'a'          => array(
   426 			'href' => true,
   418 			'href'  => true,
   427 			'title' => true,
   419 			'title' => true,
   428 		),
   420 		),
   429 		'abbr' => array(
   421 		'abbr'       => array(
   430 			'title' => true,
   422 			'title' => true,
   431 		),
   423 		),
   432 		'acronym' => array(
   424 		'acronym'    => array(
   433 			'title' => true,
   425 			'title' => true,
   434 		),
   426 		),
   435 		'b' => array(),
   427 		'b'          => array(),
   436 		'blockquote' => array(
   428 		'blockquote' => array(
   437 			'cite' => true,
   429 			'cite' => true,
   438 		),
   430 		),
   439 		'cite' => array(),
   431 		'cite'       => array(),
   440 		'code' => array(),
   432 		'code'       => array(),
   441 		'del' => array(
   433 		'del'        => array(
   442 			'datetime' => true,
   434 			'datetime' => true,
   443 		),
   435 		),
   444 		'em' => array(),
   436 		'em'         => array(),
   445 		'i' => array(),
   437 		'i'          => array(),
   446 		'q' => array(
   438 		'q'          => array(
   447 			'cite' => true,
   439 			'cite' => true,
   448 		),
   440 		),
   449 		's' => array(),
   441 		's'          => array(),
   450 		'strike' => array(),
   442 		'strike'     => array(),
   451 		'strong' => array(),
   443 		'strong'     => array(),
   452 	);
   444 	);
   453 
   445 
       
   446 	/**
       
   447 	 * @var string[] $allowedentitynames Array of KSES allowed HTML entitity names.
       
   448 	 * @since 1.0.0
       
   449 	 */
   454 	$allowedentitynames = array(
   450 	$allowedentitynames = array(
   455 		'nbsp',    'iexcl',  'cent',    'pound',  'curren', 'yen',
   451 		'nbsp',
   456 		'brvbar',  'sect',   'uml',     'copy',   'ordf',   'laquo',
   452 		'iexcl',
   457 		'not',     'shy',    'reg',     'macr',   'deg',    'plusmn',
   453 		'cent',
   458 		'acute',   'micro',  'para',    'middot', 'cedil',  'ordm',
   454 		'pound',
   459 		'raquo',   'iquest', 'Agrave',  'Aacute', 'Acirc',  'Atilde',
   455 		'curren',
   460 		'Auml',    'Aring',  'AElig',   'Ccedil', 'Egrave', 'Eacute',
   456 		'yen',
   461 		'Ecirc',   'Euml',   'Igrave',  'Iacute', 'Icirc',  'Iuml',
   457 		'brvbar',
   462 		'ETH',     'Ntilde', 'Ograve',  'Oacute', 'Ocirc',  'Otilde',
   458 		'sect',
   463 		'Ouml',    'times',  'Oslash',  'Ugrave', 'Uacute', 'Ucirc',
   459 		'uml',
   464 		'Uuml',    'Yacute', 'THORN',   'szlig',  'agrave', 'aacute',
   460 		'copy',
   465 		'acirc',   'atilde', 'auml',    'aring',  'aelig',  'ccedil',
   461 		'ordf',
   466 		'egrave',  'eacute', 'ecirc',   'euml',   'igrave', 'iacute',
   462 		'laquo',
   467 		'icirc',   'iuml',   'eth',     'ntilde', 'ograve', 'oacute',
   463 		'not',
   468 		'ocirc',   'otilde', 'ouml',    'divide', 'oslash', 'ugrave',
   464 		'shy',
   469 		'uacute',  'ucirc',  'uuml',    'yacute', 'thorn',  'yuml',
   465 		'reg',
   470 		'quot',    'amp',    'lt',      'gt',     'apos',   'OElig',
   466 		'macr',
   471 		'oelig',   'Scaron', 'scaron',  'Yuml',   'circ',   'tilde',
   467 		'deg',
   472 		'ensp',    'emsp',   'thinsp',  'zwnj',   'zwj',    'lrm',
   468 		'plusmn',
   473 		'rlm',     'ndash',  'mdash',   'lsquo',  'rsquo',  'sbquo',
   469 		'acute',
   474 		'ldquo',   'rdquo',  'bdquo',   'dagger', 'Dagger', 'permil',
   470 		'micro',
   475 		'lsaquo',  'rsaquo', 'euro',    'fnof',   'Alpha',  'Beta',
   471 		'para',
   476 		'Gamma',   'Delta',  'Epsilon', 'Zeta',   'Eta',    'Theta',
   472 		'middot',
   477 		'Iota',    'Kappa',  'Lambda',  'Mu',     'Nu',     'Xi',
   473 		'cedil',
   478 		'Omicron', 'Pi',     'Rho',     'Sigma',  'Tau',    'Upsilon',
   474 		'ordm',
   479 		'Phi',     'Chi',    'Psi',     'Omega',  'alpha',  'beta',
   475 		'raquo',
   480 		'gamma',   'delta',  'epsilon', 'zeta',   'eta',    'theta',
   476 		'iquest',
   481 		'iota',    'kappa',  'lambda',  'mu',     'nu',     'xi',
   477 		'Agrave',
   482 		'omicron', 'pi',     'rho',     'sigmaf', 'sigma',  'tau',
   478 		'Aacute',
   483 		'upsilon', 'phi',    'chi',     'psi',    'omega',  'thetasym',
   479 		'Acirc',
   484 		'upsih',   'piv',    'bull',    'hellip', 'prime',  'Prime',
   480 		'Atilde',
   485 		'oline',   'frasl',  'weierp',  'image',  'real',   'trade',
   481 		'Auml',
   486 		'alefsym', 'larr',   'uarr',    'rarr',   'darr',   'harr',
   482 		'Aring',
   487 		'crarr',   'lArr',   'uArr',    'rArr',   'dArr',   'hArr',
   483 		'AElig',
   488 		'forall',  'part',   'exist',   'empty',  'nabla',  'isin',
   484 		'Ccedil',
   489 		'notin',   'ni',     'prod',    'sum',    'minus',  'lowast',
   485 		'Egrave',
   490 		'radic',   'prop',   'infin',   'ang',    'and',    'or',
   486 		'Eacute',
   491 		'cap',     'cup',    'int',     'sim',    'cong',   'asymp',
   487 		'Ecirc',
   492 		'ne',      'equiv',  'le',      'ge',     'sub',    'sup',
   488 		'Euml',
   493 		'nsub',    'sube',   'supe',    'oplus',  'otimes', 'perp',
   489 		'Igrave',
   494 		'sdot',    'lceil',  'rceil',   'lfloor', 'rfloor', 'lang',
   490 		'Iacute',
   495 		'rang',    'loz',    'spades',  'clubs',  'hearts', 'diams',
   491 		'Icirc',
   496 		'sup1',    'sup2',   'sup3',    'frac14', 'frac12', 'frac34',
   492 		'Iuml',
       
   493 		'ETH',
       
   494 		'Ntilde',
       
   495 		'Ograve',
       
   496 		'Oacute',
       
   497 		'Ocirc',
       
   498 		'Otilde',
       
   499 		'Ouml',
       
   500 		'times',
       
   501 		'Oslash',
       
   502 		'Ugrave',
       
   503 		'Uacute',
       
   504 		'Ucirc',
       
   505 		'Uuml',
       
   506 		'Yacute',
       
   507 		'THORN',
       
   508 		'szlig',
       
   509 		'agrave',
       
   510 		'aacute',
       
   511 		'acirc',
       
   512 		'atilde',
       
   513 		'auml',
       
   514 		'aring',
       
   515 		'aelig',
       
   516 		'ccedil',
       
   517 		'egrave',
       
   518 		'eacute',
       
   519 		'ecirc',
       
   520 		'euml',
       
   521 		'igrave',
       
   522 		'iacute',
       
   523 		'icirc',
       
   524 		'iuml',
       
   525 		'eth',
       
   526 		'ntilde',
       
   527 		'ograve',
       
   528 		'oacute',
       
   529 		'ocirc',
       
   530 		'otilde',
       
   531 		'ouml',
       
   532 		'divide',
       
   533 		'oslash',
       
   534 		'ugrave',
       
   535 		'uacute',
       
   536 		'ucirc',
       
   537 		'uuml',
       
   538 		'yacute',
       
   539 		'thorn',
       
   540 		'yuml',
       
   541 		'quot',
       
   542 		'amp',
       
   543 		'lt',
       
   544 		'gt',
       
   545 		'apos',
       
   546 		'OElig',
       
   547 		'oelig',
       
   548 		'Scaron',
       
   549 		'scaron',
       
   550 		'Yuml',
       
   551 		'circ',
       
   552 		'tilde',
       
   553 		'ensp',
       
   554 		'emsp',
       
   555 		'thinsp',
       
   556 		'zwnj',
       
   557 		'zwj',
       
   558 		'lrm',
       
   559 		'rlm',
       
   560 		'ndash',
       
   561 		'mdash',
       
   562 		'lsquo',
       
   563 		'rsquo',
       
   564 		'sbquo',
       
   565 		'ldquo',
       
   566 		'rdquo',
       
   567 		'bdquo',
       
   568 		'dagger',
       
   569 		'Dagger',
       
   570 		'permil',
       
   571 		'lsaquo',
       
   572 		'rsaquo',
       
   573 		'euro',
       
   574 		'fnof',
       
   575 		'Alpha',
       
   576 		'Beta',
       
   577 		'Gamma',
       
   578 		'Delta',
       
   579 		'Epsilon',
       
   580 		'Zeta',
       
   581 		'Eta',
       
   582 		'Theta',
       
   583 		'Iota',
       
   584 		'Kappa',
       
   585 		'Lambda',
       
   586 		'Mu',
       
   587 		'Nu',
       
   588 		'Xi',
       
   589 		'Omicron',
       
   590 		'Pi',
       
   591 		'Rho',
       
   592 		'Sigma',
       
   593 		'Tau',
       
   594 		'Upsilon',
       
   595 		'Phi',
       
   596 		'Chi',
       
   597 		'Psi',
       
   598 		'Omega',
       
   599 		'alpha',
       
   600 		'beta',
       
   601 		'gamma',
       
   602 		'delta',
       
   603 		'epsilon',
       
   604 		'zeta',
       
   605 		'eta',
       
   606 		'theta',
       
   607 		'iota',
       
   608 		'kappa',
       
   609 		'lambda',
       
   610 		'mu',
       
   611 		'nu',
       
   612 		'xi',
       
   613 		'omicron',
       
   614 		'pi',
       
   615 		'rho',
       
   616 		'sigmaf',
       
   617 		'sigma',
       
   618 		'tau',
       
   619 		'upsilon',
       
   620 		'phi',
       
   621 		'chi',
       
   622 		'psi',
       
   623 		'omega',
       
   624 		'thetasym',
       
   625 		'upsih',
       
   626 		'piv',
       
   627 		'bull',
       
   628 		'hellip',
       
   629 		'prime',
       
   630 		'Prime',
       
   631 		'oline',
       
   632 		'frasl',
       
   633 		'weierp',
       
   634 		'image',
       
   635 		'real',
       
   636 		'trade',
       
   637 		'alefsym',
       
   638 		'larr',
       
   639 		'uarr',
       
   640 		'rarr',
       
   641 		'darr',
       
   642 		'harr',
       
   643 		'crarr',
       
   644 		'lArr',
       
   645 		'uArr',
       
   646 		'rArr',
       
   647 		'dArr',
       
   648 		'hArr',
       
   649 		'forall',
       
   650 		'part',
       
   651 		'exist',
       
   652 		'empty',
       
   653 		'nabla',
       
   654 		'isin',
       
   655 		'notin',
       
   656 		'ni',
       
   657 		'prod',
       
   658 		'sum',
       
   659 		'minus',
       
   660 		'lowast',
       
   661 		'radic',
       
   662 		'prop',
       
   663 		'infin',
       
   664 		'ang',
       
   665 		'and',
       
   666 		'or',
       
   667 		'cap',
       
   668 		'cup',
       
   669 		'int',
       
   670 		'sim',
       
   671 		'cong',
       
   672 		'asymp',
       
   673 		'ne',
       
   674 		'equiv',
       
   675 		'le',
       
   676 		'ge',
       
   677 		'sub',
       
   678 		'sup',
       
   679 		'nsub',
       
   680 		'sube',
       
   681 		'supe',
       
   682 		'oplus',
       
   683 		'otimes',
       
   684 		'perp',
       
   685 		'sdot',
       
   686 		'lceil',
       
   687 		'rceil',
       
   688 		'lfloor',
       
   689 		'rfloor',
       
   690 		'lang',
       
   691 		'rang',
       
   692 		'loz',
       
   693 		'spades',
       
   694 		'clubs',
       
   695 		'hearts',
       
   696 		'diams',
       
   697 		'sup1',
       
   698 		'sup2',
       
   699 		'sup3',
       
   700 		'frac14',
       
   701 		'frac12',
       
   702 		'frac34',
   497 		'there4',
   703 		'there4',
   498 	);
   704 	);
   499 
   705 
   500 	$allowedposttags = array_map( '_wp_add_global_attributes', $allowedposttags );
   706 	$allowedposttags = array_map( '_wp_add_global_attributes', $allowedposttags );
   501 } else {
   707 } else {
   502 	$allowedtags = wp_kses_array_lc( $allowedtags );
   708 	$allowedtags     = wp_kses_array_lc( $allowedtags );
   503 	$allowedposttags = wp_kses_array_lc( $allowedposttags );
   709 	$allowedposttags = wp_kses_array_lc( $allowedposttags );
   504 }
   710 }
   505 
   711 
   506 /**
   712 /**
   507  * Filters content and keeps only allowable HTML elements.
   713  * Filters text content and strips out disallowed HTML.
   508  *
   714  *
   509  * This function makes sure that only the allowed HTML element names, attribute
   715  * This function makes sure that only the allowed HTML element names, attribute
   510  * names and attribute values plus only sane HTML entities will occur in
   716  * names, attribute values, and HTML entities will occur in the given text string.
   511  * $string. You have to remove any slashes from PHP's magic quotes before you
   717  *
   512  * call this function.
   718  * This function expects unslashed data.
   513  *
   719  *
   514  * The default allowed protocols are 'http', 'https', 'ftp', 'mailto', 'news',
   720  * @see wp_kses_post() for specifically filtering post content and fields.
   515  * 'irc', 'gopher', 'nntp', 'feed', 'telnet, 'mms', 'rtsp' and 'svn'. This
   721  * @see wp_allowed_protocols() for the default allowed protocols in link URLs.
   516  * covers all common link protocols, except for 'javascript' which should not
       
   517  * be allowed for untrusted users.
       
   518  *
   722  *
   519  * @since 1.0.0
   723  * @since 1.0.0
   520  *
   724  *
   521  * @param string $string            Content to filter through kses
   725  * @param string         $string            Text content to filter.
   522  * @param array  $allowed_html      List of allowed HTML elements
   726  * @param array[]|string $allowed_html      An array of allowed HTML elements and attributes, or a
   523  * @param array  $allowed_protocols Optional. Allowed protocol in links.
   727  *                                          context name such as 'post'.
   524  * @return string Filtered content with only allowed HTML elements
   728  * @param string[]       $allowed_protocols Array of allowed URL protocols.
       
   729  * @return string Filtered content containing only the allowed HTML.
   525  */
   730  */
   526 function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) {
   731 function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) {
   527 	if ( empty( $allowed_protocols ) )
   732 	if ( empty( $allowed_protocols ) ) {
   528 		$allowed_protocols = wp_allowed_protocols();
   733 		$allowed_protocols = wp_allowed_protocols();
       
   734 	}
   529 	$string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
   735 	$string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
   530 	$string = wp_kses_normalize_entities($string);
   736 	$string = wp_kses_normalize_entities( $string );
   531 	$string = wp_kses_hook($string, $allowed_html, $allowed_protocols); // WP changed the order of these funcs and added args to wp_kses_hook
   737 	$string = wp_kses_hook( $string, $allowed_html, $allowed_protocols );
   532 	return wp_kses_split($string, $allowed_html, $allowed_protocols);
   738 	return wp_kses_split( $string, $allowed_html, $allowed_protocols );
   533 }
   739 }
   534 
   740 
   535 /**
   741 /**
   536  * Filters one attribute only and ensures its value is allowed.
   742  * Filters one HTML attribute and ensures its value is allowed.
   537  *
   743  *
   538  * This function has the advantage of being more secure than esc_attr() and can
   744  * This function can escape data in some situations where `wp_kses()` must strip the whole attribute.
   539  * escape data in some situations where wp_kses() must strip the whole attribute.
       
   540  *
   745  *
   541  * @since 4.2.3
   746  * @since 4.2.3
   542  *
   747  *
   543  * @param string $string The 'whole' attribute, including name and value.
   748  * @param string $string  The 'whole' attribute, including name and value.
   544  * @param string $element The element name to which the attribute belongs.
   749  * @param string $element The HTML element name to which the attribute belongs.
   545  * @return string Filtered attribute.
   750  * @return string Filtered attribute.
   546  */
   751  */
   547 function wp_kses_one_attr( $string, $element ) {
   752 function wp_kses_one_attr( $string, $element ) {
   548 	$uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
   753 	$uris              = wp_kses_uri_attributes();
   549 	$allowed_html = wp_kses_allowed_html( 'post' );
   754 	$allowed_html      = wp_kses_allowed_html( 'post' );
   550 	$allowed_protocols = wp_allowed_protocols();
   755 	$allowed_protocols = wp_allowed_protocols();
   551 	$string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
   756 	$string            = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
   552 	
   757 
   553 	// Preserve leading and trailing whitespace.
   758 	// Preserve leading and trailing whitespace.
   554 	$matches = array();
   759 	$matches = array();
   555 	preg_match('/^\s*/', $string, $matches);
   760 	preg_match( '/^\s*/', $string, $matches );
   556 	$lead = $matches[0];
   761 	$lead = $matches[0];
   557 	preg_match('/\s*$/', $string, $matches);
   762 	preg_match( '/\s*$/', $string, $matches );
   558 	$trail = $matches[0];
   763 	$trail = $matches[0];
   559 	if ( empty( $trail ) ) {
   764 	if ( empty( $trail ) ) {
   560 		$string = substr( $string, strlen( $lead ) );
   765 		$string = substr( $string, strlen( $lead ) );
   561 	} else {
   766 	} else {
   562 		$string = substr( $string, strlen( $lead ), -strlen( $trail ) );
   767 		$string = substr( $string, strlen( $lead ), -strlen( $trail ) );
   563 	}
   768 	}
   564 	
   769 
   565 	// Parse attribute name and value from input.
   770 	// Parse attribute name and value from input.
   566 	$split = preg_split( '/\s*=\s*/', $string, 2 );
   771 	$split = preg_split( '/\s*=\s*/', $string, 2 );
   567 	$name = $split[0];
   772 	$name  = $split[0];
   568 	if ( count( $split ) == 2 ) {
   773 	if ( count( $split ) == 2 ) {
   569 		$value = $split[1];
   774 		$value = $split[1];
   570 
   775 
   571 		// Remove quotes surrounding $value.
   776 		// Remove quotes surrounding $value.
   572 		// Also guarantee correct quoting in $string for this one attribute.
   777 		// Also guarantee correct quoting in $string for this one attribute.
   591 		if ( in_array( strtolower( $name ), $uris ) ) {
   796 		if ( in_array( strtolower( $name ), $uris ) ) {
   592 			$value = wp_kses_bad_protocol( $value, $allowed_protocols );
   797 			$value = wp_kses_bad_protocol( $value, $allowed_protocols );
   593 		}
   798 		}
   594 
   799 
   595 		$string = "$name=$quote$value$quote";
   800 		$string = "$name=$quote$value$quote";
   596 		$vless = 'n';
   801 		$vless  = 'n';
   597 	} else {
   802 	} else {
   598 		$value = '';
   803 		$value = '';
   599 		$vless = 'y';
   804 		$vless = 'y';
   600 	}
   805 	}
   601 	
   806 
   602 	// Sanitize attribute by name.
   807 	// Sanitize attribute by name.
   603 	wp_kses_attr_check( $name, $value, $string, $vless, $element, $allowed_html );
   808 	wp_kses_attr_check( $name, $value, $string, $vless, $element, $allowed_html );
   604 
   809 
   605 	// Restore whitespace.
   810 	// Restore whitespace.
   606 	return $lead . $string . $trail;
   811 	return $lead . $string . $trail;
   607 }
   812 }
   608 
   813 
   609 /**
   814 /**
   610  * Return a list of allowed tags and attributes for a given context.
   815  * Returns an array of allowed HTML tags and attributes for a given context.
   611  *
   816  *
   612  * @since 3.5.0
   817  * @since 3.5.0
       
   818  * @since 5.0.1 `form` removed as allowable HTML tag.
   613  *
   819  *
   614  * @global array $allowedposttags
   820  * @global array $allowedposttags
   615  * @global array $allowedtags
   821  * @global array $allowedtags
   616  * @global array $allowedentitynames
   822  * @global array $allowedentitynames
   617  *
   823  *
   618  * @param string|array $context The context for which to retrieve tags.
   824  * @param string|array $context The context for which to retrieve tags. Allowed values are 'post',
   619  *                              Allowed values are post, strip, data, entities, or
   825  *                              'strip', 'data', 'entities', or the name of a field filter such as
   620  *                              the name of a field filter such as pre_user_description.
   826  *                              'pre_user_description'.
   621  * @return array List of allowed tags and their allowed attributes.
   827  * @return array Array of allowed HTML tags and their allowed attributes.
   622  */
   828  */
   623 function wp_kses_allowed_html( $context = '' ) {
   829 function wp_kses_allowed_html( $context = '' ) {
   624 	global $allowedposttags, $allowedtags, $allowedentitynames;
   830 	global $allowedposttags, $allowedtags, $allowedentitynames;
   625 
   831 
   626 	if ( is_array( $context ) ) {
   832 	if ( is_array( $context ) ) {
   627 		/**
   833 		/**
   628 		 * Filters HTML elements allowed for a given context.
   834 		 * Filters the HTML that is allowed for a given context.
   629 		 *
   835 		 *
   630 		 * @since 3.5.0
   836 		 * @since 3.5.0
   631 		 *
   837 		 *
   632 		 * @param array  $context      Context to judge allowed tags by.
   838 		 * @param array[]|string $context      Context to judge allowed tags by.
   633 		 * @param string $context_type Context type (explicit).
   839 		 * @param string         $context_type Context name.
   634 		 */
   840 		 */
   635 		return apply_filters( 'wp_kses_allowed_html', $context, 'explicit' );
   841 		return apply_filters( 'wp_kses_allowed_html', $context, 'explicit' );
   636 	}
   842 	}
   637 
   843 
   638 	switch ( $context ) {
   844 	switch ( $context ) {
   639 		case 'post':
   845 		case 'post':
   640 			/** This filter is documented in wp-includes/kses.php */
   846 			/** This filter is documented in wp-includes/kses.php */
   641 			return apply_filters( 'wp_kses_allowed_html', $allowedposttags, $context );
   847 			$tags = apply_filters( 'wp_kses_allowed_html', $allowedposttags, $context );
       
   848 
       
   849 			// 5.0.1 removed the `<form>` tag, allow it if a filter is allowing it's sub-elements `<input>` or `<select>`.
       
   850 			if ( ! CUSTOM_TAGS && ! isset( $tags['form'] ) && ( isset( $tags['input'] ) || isset( $tags['select'] ) ) ) {
       
   851 				$tags = $allowedposttags;
       
   852 
       
   853 				$tags['form'] = array(
       
   854 					'action'         => true,
       
   855 					'accept'         => true,
       
   856 					'accept-charset' => true,
       
   857 					'enctype'        => true,
       
   858 					'method'         => true,
       
   859 					'name'           => true,
       
   860 					'target'         => true,
       
   861 				);
       
   862 
       
   863 				/** This filter is documented in wp-includes/kses.php */
       
   864 				$tags = apply_filters( 'wp_kses_allowed_html', $tags, $context );
       
   865 			}
       
   866 
       
   867 			return $tags;
   642 
   868 
   643 		case 'user_description':
   869 		case 'user_description':
   644 		case 'pre_user_description':
   870 		case 'pre_user_description':
   645 			$tags = $allowedtags;
   871 			$tags             = $allowedtags;
   646 			$tags['a']['rel'] = true;
   872 			$tags['a']['rel'] = true;
   647 			/** This filter is documented in wp-includes/kses.php */
   873 			/** This filter is documented in wp-includes/kses.php */
   648 			return apply_filters( 'wp_kses_allowed_html', $tags, $context );
   874 			return apply_filters( 'wp_kses_allowed_html', $tags, $context );
   649 
   875 
   650 		case 'strip':
   876 		case 'strip':
   651 			/** This filter is documented in wp-includes/kses.php */
   877 			/** This filter is documented in wp-includes/kses.php */
   652 			return apply_filters( 'wp_kses_allowed_html', array(), $context );
   878 			return apply_filters( 'wp_kses_allowed_html', array(), $context );
   653 
   879 
   654 		case 'entities':
   880 		case 'entities':
   655 			/** This filter is documented in wp-includes/kses.php */
   881 			/** This filter is documented in wp-includes/kses.php */
   656 			return apply_filters( 'wp_kses_allowed_html', $allowedentitynames, $context);
   882 			return apply_filters( 'wp_kses_allowed_html', $allowedentitynames, $context );
   657 
   883 
   658 		case 'data':
   884 		case 'data':
   659 		default:
   885 		default:
   660 			/** This filter is documented in wp-includes/kses.php */
   886 			/** This filter is documented in wp-includes/kses.php */
   661 			return apply_filters( 'wp_kses_allowed_html', $allowedtags, $context );
   887 			return apply_filters( 'wp_kses_allowed_html', $allowedtags, $context );
   662 	}
   888 	}
   663 }
   889 }
   664 
   890 
   665 /**
   891 /**
   666  * You add any kses hooks here.
   892  * You add any KSES hooks here.
   667  *
   893  *
   668  * There is currently only one kses WordPress hook, {@see 'pre_kses'}, and it is called here.
   894  * There is currently only one KSES WordPress hook, {@see 'pre_kses'}, and it is called here.
   669  * All parameters are passed to the hooks and expected to receive a string.
   895  * All parameters are passed to the hooks and expected to receive a string.
   670  *
   896  *
   671  * @since 1.0.0
   897  * @since 1.0.0
   672  *
   898  *
   673  * @param string $string            Content to filter through kses
   899  * @param string          $string            Content to filter through KSES.
   674  * @param array  $allowed_html      List of allowed HTML elements
   900  * @param array[]|string  $allowed_html      List of allowed HTML elements.
   675  * @param array  $allowed_protocols Allowed protocol in links
   901  * @param string[]        $allowed_protocols Array of allowed URL protocols.
   676  * @return string Filtered content through {@see 'pre_kses'} hook.
   902  * @return string Filtered content through {@see 'pre_kses'} hook.
   677  */
   903  */
   678 function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) {
   904 function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) {
   679 	/**
   905 	/**
   680 	 * Filters content to be run through kses.
   906 	 * Filters content to be run through kses.
   681 	 *
   907 	 *
   682 	 * @since 2.3.0
   908 	 * @since 2.3.0
   683 	 *
   909 	 *
   684 	 * @param string $string            Content to run through kses.
   910 	 * @param string          $string            Content to run through KSES.
   685 	 * @param array  $allowed_html      Allowed HTML elements.
   911 	 * @param array[]|string  $allowed_html      Allowed HTML elements.
   686 	 * @param array  $allowed_protocols Allowed protocol in links.
   912 	 * @param string[]        $allowed_protocols Array of allowed URL protocols.
   687 	 */
   913 	 */
   688 	return apply_filters( 'pre_kses', $string, $allowed_html, $allowed_protocols );
   914 	return apply_filters( 'pre_kses', $string, $allowed_html, $allowed_protocols );
   689 }
   915 }
   690 
   916 
   691 /**
   917 /**
   692  * This function returns kses' version number.
   918  * Returns the version number of KSES.
   693  *
   919  *
   694  * @since 1.0.0
   920  * @since 1.0.0
   695  *
   921  *
   696  * @return string KSES Version Number
   922  * @return string KSES version number.
   697  */
   923  */
   698 function wp_kses_version() {
   924 function wp_kses_version() {
   699 	return '0.2.2';
   925 	return '0.2.2';
   700 }
   926 }
   701 
   927 
   702 /**
   928 /**
   703  * Searches for HTML tags, no matter how malformed.
   929  * Searches for HTML tags, no matter how malformed.
   704  *
   930  *
   705  * It also matches stray ">" characters.
   931  * It also matches stray `>` characters.
   706  *
   932  *
   707  * @since 1.0.0
   933  * @since 1.0.0
   708  *
   934  *
   709  * @global array $pass_allowed_html
   935  * @global array $pass_allowed_html
   710  * @global array $pass_allowed_protocols
   936  * @global array $pass_allowed_protocols
   711  *
   937  *
   712  * @param string $string            Content to filter
   938  * @param string   $string            Content to filter.
   713  * @param array  $allowed_html      Allowed HTML elements
   939  * @param array    $allowed_html      Allowed HTML elements.
   714  * @param array  $allowed_protocols Allowed protocols to keep
   940  * @param string[] $allowed_protocols Array of allowed URL protocols.
   715  * @return string Content with fixed HTML tags
   941  * @return string Content with fixed HTML tags
   716  */
   942  */
   717 function wp_kses_split( $string, $allowed_html, $allowed_protocols ) {
   943 function wp_kses_split( $string, $allowed_html, $allowed_protocols ) {
   718 	global $pass_allowed_html, $pass_allowed_protocols;
   944 	global $pass_allowed_html, $pass_allowed_protocols;
   719 	$pass_allowed_html = $allowed_html;
   945 	$pass_allowed_html      = $allowed_html;
   720 	$pass_allowed_protocols = $allowed_protocols;
   946 	$pass_allowed_protocols = $allowed_protocols;
   721 	return preg_replace_callback( '%(<!--.*?(-->|$))|(<[^>]*(>|$)|>)%', '_wp_kses_split_callback', $string );
   947 	return preg_replace_callback( '%(<!--.*?(-->|$))|(<[^>]*(>|$)|>)%', '_wp_kses_split_callback', $string );
   722 }
   948 }
   723 
   949 
   724 /**
   950 /**
   725  * Callback for wp_kses_split.
   951  * Helper function listing HTML attributes containing a URL.
       
   952  *
       
   953  * This function returns a list of all HTML attributes that must contain
       
   954  * a URL according to the HTML specification.
       
   955  *
       
   956  * This list includes URI attributes both allowed and disallowed by KSES.
       
   957  *
       
   958  * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
       
   959  *
       
   960  * @since 5.0.1
       
   961  *
       
   962  * @return array HTML attributes that must include a URL.
       
   963  */
       
   964 function wp_kses_uri_attributes() {
       
   965 	$uri_attributes = array(
       
   966 		'action',
       
   967 		'archive',
       
   968 		'background',
       
   969 		'cite',
       
   970 		'classid',
       
   971 		'codebase',
       
   972 		'data',
       
   973 		'formaction',
       
   974 		'href',
       
   975 		'icon',
       
   976 		'longdesc',
       
   977 		'manifest',
       
   978 		'poster',
       
   979 		'profile',
       
   980 		'src',
       
   981 		'usemap',
       
   982 		'xmlns',
       
   983 	);
       
   984 
       
   985 	/**
       
   986 	 * Filters the list of attributes that are required to contain a URL.
       
   987 	 *
       
   988 	 * Use this filter to add any `data-` attributes that are required to be
       
   989 	 * validated as a URL.
       
   990 	 *
       
   991 	 * @since 5.0.1
       
   992 	 *
       
   993 	 * @param array $uri_attributes HTML attributes requiring validation as a URL.
       
   994 	 */
       
   995 	$uri_attributes = apply_filters( 'wp_kses_uri_attributes', $uri_attributes );
       
   996 
       
   997 	return $uri_attributes;
       
   998 }
       
   999 
       
  1000 /**
       
  1001  * Callback for `wp_kses_split()`.
   726  *
  1002  *
   727  * @since 3.1.0
  1003  * @since 3.1.0
   728  * @access private
  1004  * @access private
       
  1005  * @ignore
   729  *
  1006  *
   730  * @global array $pass_allowed_html
  1007  * @global array $pass_allowed_html
   731  * @global array $pass_allowed_protocols
  1008  * @global array $pass_allowed_protocols
   732  *
  1009  *
   733  * @return string
  1010  * @return string
   736 	global $pass_allowed_html, $pass_allowed_protocols;
  1013 	global $pass_allowed_html, $pass_allowed_protocols;
   737 	return wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols );
  1014 	return wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols );
   738 }
  1015 }
   739 
  1016 
   740 /**
  1017 /**
   741  * Callback for wp_kses_split for fixing malformed HTML tags.
  1018  * Callback for `wp_kses_split()` for fixing malformed HTML tags.
   742  *
  1019  *
   743  * This function does a lot of work. It rejects some very malformed things like
  1020  * This function does a lot of work. It rejects some very malformed things like
   744  * <:::>. It returns an empty string, if the element isn't allowed (look ma, no
  1021  * `<:::>`. It returns an empty string, if the element isn't allowed (look ma, no
   745  * strip_tags()!). Otherwise it splits the tag into an element and an attribute
  1022  * `strip_tags()`!). Otherwise it splits the tag into an element and an attribute
   746  * list.
  1023  * list.
   747  *
  1024  *
   748  * After the tag is split into an element and an attribute list, it is run
  1025  * After the tag is split into an element and an attribute list, it is run
   749  * through another filter which will remove illegal attributes and once that is
  1026  * through another filter which will remove illegal attributes and once that is
   750  * completed, will be returned.
  1027  * completed, will be returned.
   751  *
  1028  *
   752  * @access private
  1029  * @access private
       
  1030  * @ignore
   753  * @since 1.0.0
  1031  * @since 1.0.0
   754  *
  1032  *
   755  * @param string $string            Content to filter
  1033  * @param string   $string            Content to filter.
   756  * @param array  $allowed_html      Allowed HTML elements
  1034  * @param array    $allowed_html      Allowed HTML elements.
   757  * @param array  $allowed_protocols Allowed protocols to keep
  1035  * @param string[] $allowed_protocols Array of allowed URL protocols.
   758  * @return string Fixed HTML element
  1036  * @return string Fixed HTML element
   759  */
  1037  */
   760 function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
  1038 function wp_kses_split2( $string, $allowed_html, $allowed_protocols ) {
   761 	$string = wp_kses_stripslashes($string);
  1039 	$string = wp_kses_stripslashes( $string );
   762 
  1040 
   763 	if (substr($string, 0, 1) != '<')
  1041 	// It matched a ">" character.
       
  1042 	if ( substr( $string, 0, 1 ) != '<' ) {
   764 		return '&gt;';
  1043 		return '&gt;';
   765 	// It matched a ">" character
  1044 	}
   766 
  1045 
       
  1046 	// Allow HTML comments.
   767 	if ( '<!--' == substr( $string, 0, 4 ) ) {
  1047 	if ( '<!--' == substr( $string, 0, 4 ) ) {
   768 		$string = str_replace( array('<!--', '-->'), '', $string );
  1048 		$string = str_replace( array( '<!--', '-->' ), '', $string );
   769 		while ( $string != ($newstring = wp_kses($string, $allowed_html, $allowed_protocols)) )
  1049 		while ( $string != ( $newstring = wp_kses( $string, $allowed_html, $allowed_protocols ) ) ) {
   770 			$string = $newstring;
  1050 			$string = $newstring;
   771 		if ( $string == '' )
  1051 		}
       
  1052 		if ( $string == '' ) {
   772 			return '';
  1053 			return '';
       
  1054 		}
   773 		// prevent multiple dashes in comments
  1055 		// prevent multiple dashes in comments
   774 		$string = preg_replace('/--+/', '-', $string);
  1056 		$string = preg_replace( '/--+/', '-', $string );
   775 		// prevent three dashes closing a comment
  1057 		// prevent three dashes closing a comment
   776 		$string = preg_replace('/-$/', '', $string);
  1058 		$string = preg_replace( '/-$/', '', $string );
   777 		return "<!--{$string}-->";
  1059 		return "<!--{$string}-->";
   778 	}
  1060 	}
   779 	// Allow HTML comments
  1061 
   780 
  1062 	// It's seriously malformed.
   781 	if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9-]+)([^>]*)>?$%', $string, $matches))
  1063 	if ( ! preg_match( '%^<\s*(/\s*)?([a-zA-Z0-9-]+)([^>]*)>?$%', $string, $matches ) ) {
   782 		return '';
  1064 		return '';
   783 	// It's seriously malformed
  1065 	}
   784 
  1066 
   785 	$slash = trim($matches[1]);
  1067 	$slash    = trim( $matches[1] );
   786 	$elem = $matches[2];
  1068 	$elem     = $matches[2];
   787 	$attrlist = $matches[3];
  1069 	$attrlist = $matches[3];
   788 
  1070 
   789 	if ( ! is_array( $allowed_html ) )
  1071 	if ( ! is_array( $allowed_html ) ) {
   790 		$allowed_html = wp_kses_allowed_html( $allowed_html );
  1072 		$allowed_html = wp_kses_allowed_html( $allowed_html );
   791 
  1073 	}
   792 	if ( ! isset($allowed_html[strtolower($elem)]) )
  1074 
       
  1075 	// They are using a not allowed HTML element.
       
  1076 	if ( ! isset( $allowed_html[ strtolower( $elem ) ] ) ) {
   793 		return '';
  1077 		return '';
   794 	// They are using a not allowed HTML element
  1078 	}
   795 
  1079 
   796 	if ($slash != '')
  1080 	// No attributes are allowed for closing elements.
       
  1081 	if ( $slash != '' ) {
   797 		return "</$elem>";
  1082 		return "</$elem>";
   798 	// No attributes are allowed for closing elements
  1083 	}
   799 
  1084 
   800 	return wp_kses_attr( $elem, $attrlist, $allowed_html, $allowed_protocols );
  1085 	return wp_kses_attr( $elem, $attrlist, $allowed_html, $allowed_protocols );
   801 }
  1086 }
   802 
  1087 
   803 /**
  1088 /**
   804  * Removes all attributes, if none are allowed for this element.
  1089  * Removes all attributes, if none are allowed for this element.
   805  *
  1090  *
   806  * If some are allowed it calls wp_kses_hair() to split them further, and then
  1091  * If some are allowed it calls `wp_kses_hair()` to split them further, and then
   807  * it builds up new HTML code from the data that kses_hair() returns. It also
  1092  * it builds up new HTML code from the data that `kses_hair()` returns. It also
   808  * removes "<" and ">" characters, if there are any left. One more thing it does
  1093  * removes `<` and `>` characters, if there are any left. One more thing it does
   809  * is to check if the tag has a closing XHTML slash, and if it does, it puts one
  1094  * is to check if the tag has a closing XHTML slash, and if it does, it puts one
   810  * in the returned code as well.
  1095  * in the returned code as well.
   811  *
  1096  *
   812  * @since 1.0.0
  1097  * @since 1.0.0
   813  *
  1098  *
   814  * @param string $element           HTML element/tag
  1099  * @param string   $element           HTML element/tag.
   815  * @param string $attr              HTML attributes from HTML element to closing HTML element tag
  1100  * @param string   $attr              HTML attributes from HTML element to closing HTML element tag.
   816  * @param array  $allowed_html      Allowed HTML elements
  1101  * @param array    $allowed_html      Allowed HTML elements.
   817  * @param array  $allowed_protocols Allowed protocols to keep
  1102  * @param string[] $allowed_protocols Array of allowed URL protocols.
   818  * @return string Sanitized HTML element
  1103  * @return string Sanitized HTML element.
   819  */
  1104  */
   820 function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
  1105 function wp_kses_attr( $element, $attr, $allowed_html, $allowed_protocols ) {
   821 	if ( ! is_array( $allowed_html ) )
  1106 	if ( ! is_array( $allowed_html ) ) {
   822 		$allowed_html = wp_kses_allowed_html( $allowed_html );
  1107 		$allowed_html = wp_kses_allowed_html( $allowed_html );
       
  1108 	}
   823 
  1109 
   824 	// Is there a closing XHTML slash at the end of the attributes?
  1110 	// Is there a closing XHTML slash at the end of the attributes?
   825 	$xhtml_slash = '';
  1111 	$xhtml_slash = '';
   826 	if (preg_match('%\s*/\s*$%', $attr))
  1112 	if ( preg_match( '%\s*/\s*$%', $attr ) ) {
   827 		$xhtml_slash = ' /';
  1113 		$xhtml_slash = ' /';
       
  1114 	}
   828 
  1115 
   829 	// Are any attributes allowed at all for this element?
  1116 	// Are any attributes allowed at all for this element?
   830 	$element_low = strtolower( $element );
  1117 	$element_low = strtolower( $element );
   831 	if ( empty( $allowed_html[ $element_low ] ) || true === $allowed_html[ $element_low ] ) {
  1118 	if ( empty( $allowed_html[ $element_low ] ) || true === $allowed_html[ $element_low ] ) {
   832 		return "<$element$xhtml_slash>";
  1119 		return "<$element$xhtml_slash>";
   833 	}
  1120 	}
   834 
  1121 
   835 	// Split it
  1122 	// Split it
   836 	$attrarr = wp_kses_hair($attr, $allowed_protocols);
  1123 	$attrarr = wp_kses_hair( $attr, $allowed_protocols );
   837 
  1124 
   838 	// Go through $attrarr, and save the allowed attributes for this element
  1125 	// Go through $attrarr, and save the allowed attributes for this element
   839 	// in $attr2
  1126 	// in $attr2
   840 	$attr2 = '';
  1127 	$attr2 = '';
   841 	foreach ( $attrarr as $arreach ) {
  1128 	foreach ( $attrarr as $arreach ) {
   842 		if ( wp_kses_attr_check( $arreach['name'], $arreach['value'], $arreach['whole'], $arreach['vless'], $element, $allowed_html ) ) {
  1129 		if ( wp_kses_attr_check( $arreach['name'], $arreach['value'], $arreach['whole'], $arreach['vless'], $element, $allowed_html ) ) {
   843 			$attr2 .= ' '.$arreach['whole'];
  1130 			$attr2 .= ' ' . $arreach['whole'];
   844 		}
  1131 		}
   845 	}
  1132 	}
   846 
  1133 
   847 	// Remove any "<" or ">" characters
  1134 	// Remove any "<" or ">" characters
   848 	$attr2 = preg_replace('/[<>]/', '', $attr2);
  1135 	$attr2 = preg_replace( '/[<>]/', '', $attr2 );
   849 
  1136 
   850 	return "<$element$attr2$xhtml_slash>";
  1137 	return "<$element$attr2$xhtml_slash>";
   851 }
  1138 }
   852 
  1139 
   853 /**
  1140 /**
   854  * Determine whether an attribute is allowed.
  1141  * Determines whether an attribute is allowed.
   855  *
  1142  *
   856  * @since 4.2.3
  1143  * @since 4.2.3
   857  *
  1144  * @since 5.0.0 Add support for `data-*` wildcard attributes.
   858  * @param string $name The attribute name. Returns empty string when not allowed.
  1145  *
   859  * @param string $value The attribute value. Returns a filtered value.
  1146  * @param string $name         The attribute name. Passed by reference. Returns empty string when not allowed.
   860  * @param string $whole The name=value input. Returns filtered input.
  1147  * @param string $value        The attribute value. Passed by reference. Returns a filtered value.
   861  * @param string $vless 'y' when attribute like "enabled", otherwise 'n'.
  1148  * @param string $whole        The `name=value` input. Passed by reference. Returns filtered input.
   862  * @param string $element The name of the element to which this attribute belongs.
  1149  * @param string $vless        Whether the attribute is valueless. Use 'y' or 'n'.
   863  * @param array $allowed_html The full list of allowed elements and attributes.
  1150  * @param string $element      The name of the element to which this attribute belongs.
   864  * @return bool Is the attribute allowed?
  1151  * @param array  $allowed_html The full list of allowed elements and attributes.
       
  1152  * @return bool Whether or not the attribute is allowed.
   865  */
  1153  */
   866 function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowed_html ) {
  1154 function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowed_html ) {
   867 	$allowed_attr = $allowed_html[strtolower( $element )];
  1155 	$allowed_attr = $allowed_html[ strtolower( $element ) ];
   868 
  1156 
   869 	$name_low = strtolower( $name );
  1157 	$name_low = strtolower( $name );
   870 	if ( ! isset( $allowed_attr[$name_low] ) || '' == $allowed_attr[$name_low] ) {
  1158 	if ( ! isset( $allowed_attr[ $name_low ] ) || '' == $allowed_attr[ $name_low ] ) {
   871 		$name = $value = $whole = '';
  1159 		/*
   872 		return false;
  1160 		 * Allow `data-*` attributes.
       
  1161 		 *
       
  1162 		 * When specifying `$allowed_html`, the attribute name should be set as
       
  1163 		 * `data-*` (not to be mixed with the HTML 4.0 `data` attribute, see
       
  1164 		 * https://www.w3.org/TR/html40/struct/objects.html#adef-data).
       
  1165 		 *
       
  1166 		 * Note: the attribute name should only contain `A-Za-z0-9_-` chars,
       
  1167 		 * double hyphens `--` are not accepted by WordPress.
       
  1168 		 */
       
  1169 		if ( strpos( $name_low, 'data-' ) === 0 && ! empty( $allowed_attr['data-*'] ) && preg_match( '/^data(?:-[a-z0-9_]+)+$/', $name_low, $match ) ) {
       
  1170 			/*
       
  1171 			 * Add the whole attribute name to the allowed attributes and set any restrictions
       
  1172 			 * for the `data-*` attribute values for the current element.
       
  1173 			 */
       
  1174 			$allowed_attr[ $match[0] ] = $allowed_attr['data-*'];
       
  1175 		} else {
       
  1176 			$name = $value = $whole = '';
       
  1177 			return false;
       
  1178 		}
   873 	}
  1179 	}
   874 
  1180 
   875 	if ( 'style' == $name_low ) {
  1181 	if ( 'style' == $name_low ) {
   876 		$new_value = safecss_filter_attr( $value );
  1182 		$new_value = safecss_filter_attr( $value );
   877 
  1183 
   882 
  1188 
   883 		$whole = str_replace( $value, $new_value, $whole );
  1189 		$whole = str_replace( $value, $new_value, $whole );
   884 		$value = $new_value;
  1190 		$value = $new_value;
   885 	}
  1191 	}
   886 
  1192 
   887 	if ( is_array( $allowed_attr[$name_low] ) ) {
  1193 	if ( is_array( $allowed_attr[ $name_low ] ) ) {
   888 		// there are some checks
  1194 		// there are some checks
   889 		foreach ( $allowed_attr[$name_low] as $currkey => $currval ) {
  1195 		foreach ( $allowed_attr[ $name_low ] as $currkey => $currval ) {
   890 			if ( ! wp_kses_check_attr_val( $value, $vless, $currkey, $currval ) ) {
  1196 			if ( ! wp_kses_check_attr_val( $value, $vless, $currkey, $currval ) ) {
   891 				$name = $value = $whole = '';
  1197 				$name = $value = $whole = '';
   892 				return false;
  1198 				return false;
   893 			}
  1199 			}
   894 		}
  1200 		}
   904  * with attribute data, and tries to do the right thing even if it gets weird
  1210  * with attribute data, and tries to do the right thing even if it gets weird
   905  * input. It will add quotes around attribute values that don't have any quotes
  1211  * input. It will add quotes around attribute values that don't have any quotes
   906  * or apostrophes around them, to make it easier to produce HTML code that will
  1212  * or apostrophes around them, to make it easier to produce HTML code that will
   907  * conform to W3C's HTML specification. It will also remove bad URL protocols
  1213  * conform to W3C's HTML specification. It will also remove bad URL protocols
   908  * from attribute values. It also reduces duplicate attributes by using the
  1214  * from attribute values. It also reduces duplicate attributes by using the
   909  * attribute defined first (foo='bar' foo='baz' will result in foo='bar').
  1215  * attribute defined first (`foo='bar' foo='baz'` will result in `foo='bar'`).
   910  *
  1216  *
   911  * @since 1.0.0
  1217  * @since 1.0.0
   912  *
  1218  *
   913  * @param string $attr              Attribute list from HTML element to closing HTML element tag
  1219  * @param string   $attr              Attribute list from HTML element to closing HTML element tag.
   914  * @param array  $allowed_protocols Allowed protocols to keep
  1220  * @param string[] $allowed_protocols Array of allowed URL protocols.
   915  * @return array List of attributes after parsing
  1221  * @return array[] Array of attribute information after parsing.
   916  */
  1222  */
   917 function wp_kses_hair($attr, $allowed_protocols) {
  1223 function wp_kses_hair( $attr, $allowed_protocols ) {
   918 	$attrarr = array();
  1224 	$attrarr  = array();
   919 	$mode = 0;
  1225 	$mode     = 0;
   920 	$attrname = '';
  1226 	$attrname = '';
   921 	$uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
  1227 	$uris     = wp_kses_uri_attributes();
   922 
  1228 
   923 	// Loop through the whole attribute list
  1229 	// Loop through the whole attribute list
   924 
  1230 
   925 	while (strlen($attr) != 0) {
  1231 	while ( strlen( $attr ) != 0 ) {
   926 		$working = 0; // Was the last operation successful?
  1232 		$working = 0; // Was the last operation successful?
   927 
  1233 
   928 		switch ($mode) {
  1234 		switch ( $mode ) {
   929 			case 0 : // attribute name, href for instance
  1235 			case 0:
   930 
  1236 				if ( preg_match( '/^([-a-zA-Z:]+)/', $attr, $match ) ) {
   931 				if ( preg_match('/^([-a-zA-Z:]+)/', $attr, $match ) ) {
       
   932 					$attrname = $match[1];
  1237 					$attrname = $match[1];
   933 					$working = $mode = 1;
  1238 					$working  = $mode = 1;
   934 					$attr = preg_replace( '/^[-a-zA-Z:]+/', '', $attr );
  1239 					$attr     = preg_replace( '/^[-a-zA-Z:]+/', '', $attr );
   935 				}
  1240 				}
   936 
  1241 
   937 				break;
  1242 				break;
   938 
  1243 
   939 			case 1 : // equals sign or valueless ("selected")
  1244 			case 1:
   940 
  1245 				if ( preg_match( '/^\s*=\s*/', $attr ) ) { // equals sign
   941 				if (preg_match('/^\s*=\s*/', $attr)) // equals sign
       
   942 					{
       
   943 					$working = 1;
  1246 					$working = 1;
   944 					$mode = 2;
  1247 					$mode    = 2;
   945 					$attr = preg_replace('/^\s*=\s*/', '', $attr);
  1248 					$attr    = preg_replace( '/^\s*=\s*/', '', $attr );
   946 					break;
  1249 					break;
   947 				}
  1250 				}
   948 
  1251 
   949 				if (preg_match('/^\s+/', $attr)) // valueless
  1252 				if ( preg_match( '/^\s+/', $attr ) ) { // valueless
   950 					{
       
   951 					$working = 1;
  1253 					$working = 1;
   952 					$mode = 0;
  1254 					$mode    = 0;
   953 					if(false === array_key_exists($attrname, $attrarr)) {
  1255 					if ( false === array_key_exists( $attrname, $attrarr ) ) {
   954 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
  1256 						$attrarr[ $attrname ] = array(
       
  1257 							'name'  => $attrname,
       
  1258 							'value' => '',
       
  1259 							'whole' => $attrname,
       
  1260 							'vless' => 'y',
       
  1261 						);
   955 					}
  1262 					}
   956 					$attr = preg_replace('/^\s+/', '', $attr);
  1263 					$attr = preg_replace( '/^\s+/', '', $attr );
   957 				}
  1264 				}
   958 
  1265 
   959 				break;
  1266 				break;
   960 
  1267 
   961 			case 2 : // attribute value, a URL after href= for instance
  1268 			case 2:
   962 
  1269 				if ( preg_match( '%^"([^"]*)"(\s+|/?$)%', $attr, $match ) ) {
   963 				if (preg_match('%^"([^"]*)"(\s+|/?$)%', $attr, $match))
       
   964 					// "value"
  1270 					// "value"
   965 					{
       
   966 					$thisval = $match[1];
  1271 					$thisval = $match[1];
   967 					if ( in_array(strtolower($attrname), $uris) )
  1272 					if ( in_array( strtolower( $attrname ), $uris ) ) {
   968 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
  1273 						$thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
   969 
  1274 					}
   970 					if(false === array_key_exists($attrname, $attrarr)) {
  1275 
   971 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
  1276 					if ( false === array_key_exists( $attrname, $attrarr ) ) {
       
  1277 						$attrarr[ $attrname ] = array(
       
  1278 							'name'  => $attrname,
       
  1279 							'value' => $thisval,
       
  1280 							'whole' => "$attrname=\"$thisval\"",
       
  1281 							'vless' => 'n',
       
  1282 						);
   972 					}
  1283 					}
   973 					$working = 1;
  1284 					$working = 1;
   974 					$mode = 0;
  1285 					$mode    = 0;
   975 					$attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
  1286 					$attr    = preg_replace( '/^"[^"]*"(\s+|$)/', '', $attr );
   976 					break;
  1287 					break;
   977 				}
  1288 				}
   978 
  1289 
   979 				if (preg_match("%^'([^']*)'(\s+|/?$)%", $attr, $match))
  1290 				if ( preg_match( "%^'([^']*)'(\s+|/?$)%", $attr, $match ) ) {
   980 					// 'value'
  1291 					// 'value'
   981 					{
       
   982 					$thisval = $match[1];
  1292 					$thisval = $match[1];
   983 					if ( in_array(strtolower($attrname), $uris) )
  1293 					if ( in_array( strtolower( $attrname ), $uris ) ) {
   984 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
  1294 						$thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
   985 
  1295 					}
   986 					if(false === array_key_exists($attrname, $attrarr)) {
  1296 
   987 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
  1297 					if ( false === array_key_exists( $attrname, $attrarr ) ) {
       
  1298 						$attrarr[ $attrname ] = array(
       
  1299 							'name'  => $attrname,
       
  1300 							'value' => $thisval,
       
  1301 							'whole' => "$attrname='$thisval'",
       
  1302 							'vless' => 'n',
       
  1303 						);
   988 					}
  1304 					}
   989 					$working = 1;
  1305 					$working = 1;
   990 					$mode = 0;
  1306 					$mode    = 0;
   991 					$attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
  1307 					$attr    = preg_replace( "/^'[^']*'(\s+|$)/", '', $attr );
   992 					break;
  1308 					break;
   993 				}
  1309 				}
   994 
  1310 
   995 				if (preg_match("%^([^\s\"']+)(\s+|/?$)%", $attr, $match))
  1311 				if ( preg_match( "%^([^\s\"']+)(\s+|/?$)%", $attr, $match ) ) {
   996 					// value
  1312 					// value
   997 					{
       
   998 					$thisval = $match[1];
  1313 					$thisval = $match[1];
   999 					if ( in_array(strtolower($attrname), $uris) )
  1314 					if ( in_array( strtolower( $attrname ), $uris ) ) {
  1000 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
  1315 						$thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
  1001 
  1316 					}
  1002 					if(false === array_key_exists($attrname, $attrarr)) {
  1317 
  1003 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
  1318 					if ( false === array_key_exists( $attrname, $attrarr ) ) {
       
  1319 						$attrarr[ $attrname ] = array(
       
  1320 							'name'  => $attrname,
       
  1321 							'value' => $thisval,
       
  1322 							'whole' => "$attrname=\"$thisval\"",
       
  1323 							'vless' => 'n',
       
  1324 						);
  1004 					}
  1325 					}
  1005 					// We add quotes to conform to W3C's HTML spec.
  1326 					// We add quotes to conform to W3C's HTML spec.
  1006 					$working = 1;
  1327 					$working = 1;
  1007 					$mode = 0;
  1328 					$mode    = 0;
  1008 					$attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
  1329 					$attr    = preg_replace( "%^[^\s\"']+(\s+|$)%", '', $attr );
  1009 				}
  1330 				}
  1010 
  1331 
  1011 				break;
  1332 				break;
  1012 		} // switch
  1333 		} // switch
  1013 
  1334 
  1014 		if ($working == 0) // not well formed, remove and try again
  1335 		if ( $working == 0 ) { // not well formed, remove and try again
  1015 		{
  1336 			$attr = wp_kses_html_error( $attr );
  1016 			$attr = wp_kses_html_error($attr);
       
  1017 			$mode = 0;
  1337 			$mode = 0;
  1018 		}
  1338 		}
  1019 	} // while
  1339 	} // while
  1020 
  1340 
  1021 	if ($mode == 1 && false === array_key_exists($attrname, $attrarr))
  1341 	if ( $mode == 1 && false === array_key_exists( $attrname, $attrarr ) ) {
  1022 		// special case, for when the attribute list ends with a valueless
  1342 		// special case, for when the attribute list ends with a valueless
  1023 		// attribute like "selected"
  1343 		// attribute like "selected"
  1024 		$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
  1344 		$attrarr[ $attrname ] = array(
       
  1345 			'name'  => $attrname,
       
  1346 			'value' => '',
       
  1347 			'whole' => $attrname,
       
  1348 			'vless' => 'y',
       
  1349 		);
       
  1350 	}
  1025 
  1351 
  1026 	return $attrarr;
  1352 	return $attrarr;
  1027 }
  1353 }
  1028 
  1354 
  1029 /**
  1355 /**
  1030  * Finds all attributes of an HTML element.
  1356  * Finds all attributes of an HTML element.
  1031  *
  1357  *
  1032  * Does not modify input.  May return "evil" output.
  1358  * Does not modify input.  May return "evil" output.
  1033  *
  1359  *
  1034  * Based on wp_kses_split2() and wp_kses_attr()
  1360  * Based on `wp_kses_split2()` and `wp_kses_attr()`.
  1035  *
  1361  *
  1036  * @since 4.2.3
  1362  * @since 4.2.3
  1037  *
  1363  *
  1038  * @param string $element HTML element/tag
  1364  * @param string $element HTML element.
  1039  * @return array|bool List of attributes found in $element. Returns false on failure.
  1365  * @return array|bool List of attributes found in the element. Returns false on failure.
  1040  */
  1366  */
  1041 function wp_kses_attr_parse( $element ) {
  1367 function wp_kses_attr_parse( $element ) {
  1042 	$valid = preg_match('%^(<\s*)(/\s*)?([a-zA-Z0-9]+\s*)([^>]*)(>?)$%', $element, $matches);
  1368 	$valid = preg_match( '%^(<\s*)(/\s*)?([a-zA-Z0-9]+\s*)([^>]*)(>?)$%', $element, $matches );
  1043 	if ( 1 !== $valid ) {
  1369 	if ( 1 !== $valid ) {
  1044 		return false;
  1370 		return false;
  1045 	}
  1371 	}
  1046 
  1372 
  1047 	$begin =  $matches[1];
  1373 	$begin  = $matches[1];
  1048 	$slash =  $matches[2];
  1374 	$slash  = $matches[2];
  1049 	$elname = $matches[3];
  1375 	$elname = $matches[3];
  1050 	$attr =   $matches[4];
  1376 	$attr   = $matches[4];
  1051 	$end =    $matches[5];
  1377 	$end    = $matches[5];
  1052 
  1378 
  1053 	if ( '' !== $slash ) {
  1379 	if ( '' !== $slash ) {
  1054 		// Closing elements do not get parsed.
  1380 		// Closing elements do not get parsed.
  1055 		return false;
  1381 		return false;
  1056 	}
  1382 	}
  1057 
  1383 
  1058 	// Is there a closing XHTML slash at the end of the attributes?
  1384 	// Is there a closing XHTML slash at the end of the attributes?
  1059 	if ( 1 === preg_match( '%\s*/\s*$%', $attr, $matches ) ) {
  1385 	if ( 1 === preg_match( '%\s*/\s*$%', $attr, $matches ) ) {
  1060 		$xhtml_slash = $matches[0];
  1386 		$xhtml_slash = $matches[0];
  1061 		$attr = substr( $attr, 0, -strlen( $xhtml_slash ) );
  1387 		$attr        = substr( $attr, 0, -strlen( $xhtml_slash ) );
  1062 	} else {
  1388 	} else {
  1063 		$xhtml_slash = '';
  1389 		$xhtml_slash = '';
  1064 	}
  1390 	}
  1065 	
  1391 
  1066 	// Split it
  1392 	// Split it
  1067 	$attrarr = wp_kses_hair_parse( $attr );
  1393 	$attrarr = wp_kses_hair_parse( $attr );
  1068 	if ( false === $attrarr ) {
  1394 	if ( false === $attrarr ) {
  1069 		return false;
  1395 		return false;
  1070 	}
  1396 	}
  1071 
  1397 
  1072 	// Make sure all input is returned by adding front and back matter.
  1398 	// Make sure all input is returned by adding front and back matter.
  1073 	array_unshift( $attrarr, $begin . $slash . $elname );
  1399 	array_unshift( $attrarr, $begin . $slash . $elname );
  1074 	array_push( $attrarr, $xhtml_slash . $end );
  1400 	array_push( $attrarr, $xhtml_slash . $end );
  1075 	
  1401 
  1076 	return $attrarr;
  1402 	return $attrarr;
  1077 }
  1403 }
  1078 
  1404 
  1079 /**
  1405 /**
  1080  * Builds an attribute list from string containing attributes.
  1406  * Builds an attribute list from string containing attributes.
  1081  *
  1407  *
  1082  * Does not modify input.  May return "evil" output.
  1408  * Does not modify input.  May return "evil" output.
  1083  * In case of unexpected input, returns false instead of stripping things.
  1409  * In case of unexpected input, returns false instead of stripping things.
  1084  *
  1410  *
  1085  * Based on wp_kses_hair() but does not return a multi-dimensional array.
  1411  * Based on `wp_kses_hair()` but does not return a multi-dimensional array.
  1086  *
  1412  *
  1087  * @since 4.2.3
  1413  * @since 4.2.3
  1088  *
  1414  *
  1089  * @param string $attr Attribute list from HTML element to closing HTML element tag
  1415  * @param string $attr Attribute list from HTML element to closing HTML element tag.
  1090  * @return array|bool List of attributes found in $attr. Returns false on failure.
  1416  * @return array|bool List of attributes found in $attr. Returns false on failure.
  1091  */
  1417  */
  1092 function wp_kses_hair_parse( $attr ) {
  1418 function wp_kses_hair_parse( $attr ) {
  1093 	if ( '' === $attr ) {
  1419 	if ( '' === $attr ) {
  1094 		return array();
  1420 		return array();
  1095 	}
  1421 	}
  1096 
  1422 
       
  1423 	// phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation
  1097 	$regex =
  1424 	$regex =
  1098 	  '(?:'
  1425 	'(?:'
  1099 	.     '[-a-zA-Z:]+'   // Attribute name.
  1426 	.     '[-a-zA-Z:]+'   // Attribute name.
  1100 	. '|'
  1427 	. '|'
  1101 	.     '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html.
  1428 	.     '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html.
  1102 	. ')'
  1429 	. ')'
  1103 	. '(?:'               // Attribute value.
  1430 	. '(?:'               // Attribute value.
  1112 	.     ')'
  1439 	.     ')'
  1113 	. '|'
  1440 	. '|'
  1114 	.     '(?:\s|$)'      // If attribute has no value, space is required.
  1441 	.     '(?:\s|$)'      // If attribute has no value, space is required.
  1115 	. ')'
  1442 	. ')'
  1116 	. '\s*';              // Trailing space is optional except as mentioned above.
  1443 	. '\s*';              // Trailing space is optional except as mentioned above.
       
  1444 	// phpcs:enable
  1117 
  1445 
  1118 	// Although it is possible to reduce this procedure to a single regexp,
  1446 	// Although it is possible to reduce this procedure to a single regexp,
  1119 	// we must run that regexp twice to get exactly the expected result.
  1447 	// we must run that regexp twice to get exactly the expected result.
  1120 
  1448 
  1121 	$validation = "%^($regex)+$%";
  1449 	$validation = "%^($regex)+$%";
  1130 }
  1458 }
  1131 
  1459 
  1132 /**
  1460 /**
  1133  * Performs different checks for attribute values.
  1461  * Performs different checks for attribute values.
  1134  *
  1462  *
  1135  * The currently implemented checks are "maxlen", "minlen", "maxval", "minval"
  1463  * The currently implemented checks are "maxlen", "minlen", "maxval", "minval",
  1136  * and "valueless".
  1464  * and "valueless".
  1137  *
  1465  *
  1138  * @since 1.0.0
  1466  * @since 1.0.0
  1139  *
  1467  *
  1140  * @param string $value      Attribute value
  1468  * @param string $value      Attribute value.
  1141  * @param string $vless      Whether the value is valueless. Use 'y' or 'n'
  1469  * @param string $vless      Whether the attribute is valueless. Use 'y' or 'n'.
  1142  * @param string $checkname  What $checkvalue is checking for.
  1470  * @param string $checkname  What $checkvalue is checking for.
  1143  * @param mixed  $checkvalue What constraint the value should pass
  1471  * @param mixed  $checkvalue What constraint the value should pass.
  1144  * @return bool Whether check passes
  1472  * @return bool Whether check passes.
  1145  */
  1473  */
  1146 function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) {
  1474 function wp_kses_check_attr_val( $value, $vless, $checkname, $checkvalue ) {
  1147 	$ok = true;
  1475 	$ok = true;
  1148 
  1476 
  1149 	switch (strtolower($checkname)) {
  1477 	switch ( strtolower( $checkname ) ) {
  1150 		case 'maxlen' :
  1478 		case 'maxlen':
  1151 			// The maxlen check makes sure that the attribute value has a length not
  1479 			// The maxlen check makes sure that the attribute value has a length not
  1152 			// greater than the given value. This can be used to avoid Buffer Overflows
  1480 			// greater than the given value. This can be used to avoid Buffer Overflows
  1153 			// in WWW clients and various Internet servers.
  1481 			// in WWW clients and various Internet servers.
  1154 
  1482 
  1155 			if (strlen($value) > $checkvalue)
  1483 			if ( strlen( $value ) > $checkvalue ) {
  1156 				$ok = false;
  1484 				$ok = false;
       
  1485 			}
  1157 			break;
  1486 			break;
  1158 
  1487 
  1159 		case 'minlen' :
  1488 		case 'minlen':
  1160 			// The minlen check makes sure that the attribute value has a length not
  1489 			// The minlen check makes sure that the attribute value has a length not
  1161 			// smaller than the given value.
  1490 			// smaller than the given value.
  1162 
  1491 
  1163 			if (strlen($value) < $checkvalue)
  1492 			if ( strlen( $value ) < $checkvalue ) {
  1164 				$ok = false;
  1493 				$ok = false;
       
  1494 			}
  1165 			break;
  1495 			break;
  1166 
  1496 
  1167 		case 'maxval' :
  1497 		case 'maxval':
  1168 			// The maxval check does two things: it checks that the attribute value is
  1498 			// The maxval check does two things: it checks that the attribute value is
  1169 			// an integer from 0 and up, without an excessive amount of zeroes or
  1499 			// an integer from 0 and up, without an excessive amount of zeroes or
  1170 			// whitespace (to avoid Buffer Overflows). It also checks that the attribute
  1500 			// whitespace (to avoid Buffer Overflows). It also checks that the attribute
  1171 			// value is not greater than the given value.
  1501 			// value is not greater than the given value.
  1172 			// This check can be used to avoid Denial of Service attacks.
  1502 			// This check can be used to avoid Denial of Service attacks.
  1173 
  1503 
  1174 			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
  1504 			if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
  1175 				$ok = false;
  1505 				$ok = false;
  1176 			if ($value > $checkvalue)
  1506 			}
       
  1507 			if ( $value > $checkvalue ) {
  1177 				$ok = false;
  1508 				$ok = false;
       
  1509 			}
  1178 			break;
  1510 			break;
  1179 
  1511 
  1180 		case 'minval' :
  1512 		case 'minval':
  1181 			// The minval check makes sure that the attribute value is a positive integer,
  1513 			// The minval check makes sure that the attribute value is a positive integer,
  1182 			// and that it is not smaller than the given value.
  1514 			// and that it is not smaller than the given value.
  1183 
  1515 
  1184 			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
  1516 			if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
  1185 				$ok = false;
  1517 				$ok = false;
  1186 			if ($value < $checkvalue)
  1518 			}
       
  1519 			if ( $value < $checkvalue ) {
  1187 				$ok = false;
  1520 				$ok = false;
       
  1521 			}
  1188 			break;
  1522 			break;
  1189 
  1523 
  1190 		case 'valueless' :
  1524 		case 'valueless':
  1191 			// The valueless check makes sure if the attribute has a value
  1525 			// The valueless check makes sure if the attribute has a value
  1192 			// (like <a href="blah">) or not (<option selected>). If the given value
  1526 			// (like `<a href="blah">`) or not (`<option selected>`). If the given value
  1193 			// is a "y" or a "Y", the attribute must not have a value.
  1527 			// is a "y" or a "Y", the attribute must not have a value.
  1194 			// If the given value is an "n" or an "N", the attribute must have one.
  1528 			// If the given value is an "n" or an "N", the attribute must have a value.
  1195 
  1529 
  1196 			if (strtolower($checkvalue) != $vless)
  1530 			if ( strtolower( $checkvalue ) != $vless ) {
  1197 				$ok = false;
  1531 				$ok = false;
       
  1532 			}
  1198 			break;
  1533 			break;
  1199 	} // switch
  1534 	} // switch
  1200 
  1535 
  1201 	return $ok;
  1536 	return $ok;
  1202 }
  1537 }
  1203 
  1538 
  1204 /**
  1539 /**
  1205  * Sanitize string from bad protocols.
  1540  * Sanitizes a string and removed disallowed URL protocols.
  1206  *
  1541  *
  1207  * This function removes all non-allowed protocols from the beginning of
  1542  * This function removes all non-allowed protocols from the beginning of the
  1208  * $string. It ignores whitespace and the case of the letters, and it does
  1543  * string. It ignores whitespace and the case of the letters, and it does
  1209  * understand HTML entities. It does its work in a while loop, so it won't be
  1544  * understand HTML entities. It does its work recursively, so it won't be
  1210  * fooled by a string like "javascript:javascript:alert(57)".
  1545  * fooled by a string like `javascript:javascript:alert(57)`.
  1211  *
  1546  *
  1212  * @since 1.0.0
  1547  * @since 1.0.0
  1213  *
  1548  *
  1214  * @param string $string            Content to filter bad protocols from
  1549  * @param string   $string            Content to filter bad protocols from.
  1215  * @param array  $allowed_protocols Allowed protocols to keep
  1550  * @param string[] $allowed_protocols Array of allowed URL protocols.
  1216  * @return string Filtered content
  1551  * @return string Filtered content.
  1217  */
  1552  */
  1218 function wp_kses_bad_protocol($string, $allowed_protocols) {
  1553 function wp_kses_bad_protocol( $string, $allowed_protocols ) {
  1219 	$string = wp_kses_no_null($string);
  1554 	$string     = wp_kses_no_null( $string );
  1220 	$iterations = 0;
  1555 	$iterations = 0;
  1221 
  1556 
  1222 	do {
  1557 	do {
  1223 		$original_string = $string;
  1558 		$original_string = $string;
  1224 		$string = wp_kses_bad_protocol_once($string, $allowed_protocols);
  1559 		$string          = wp_kses_bad_protocol_once( $string, $allowed_protocols );
  1225 	} while ( $original_string != $string && ++$iterations < 6 );
  1560 	} while ( $original_string != $string && ++$iterations < 6 );
  1226 
  1561 
  1227 	if ( $original_string != $string )
  1562 	if ( $original_string != $string ) {
  1228 		return '';
  1563 		return '';
       
  1564 	}
  1229 
  1565 
  1230 	return $string;
  1566 	return $string;
  1231 }
  1567 }
  1232 
  1568 
  1233 /**
  1569 /**
  1234  * Removes any invalid control characters in $string.
  1570  * Removes any invalid control characters in a text string.
  1235  *
  1571  *
  1236  * Also removes any instance of the '\0' string.
  1572  * Also removes any instance of the `\0` string.
  1237  *
  1573  *
  1238  * @since 1.0.0
  1574  * @since 1.0.0
  1239  *
  1575  *
  1240  * @param string $string
  1576  * @param string $string  Content to filter null characters from.
  1241  * @param array $options Set 'slash_zero' => 'keep' when '\0' is allowed. Default is 'remove'.
  1577  * @param array  $options Set 'slash_zero' => 'keep' when '\0' is allowed. Default is 'remove'.
  1242  * @return string
  1578  * @return string Filtered content.
  1243  */
  1579  */
  1244 function wp_kses_no_null( $string, $options = null ) {
  1580 function wp_kses_no_null( $string, $options = null ) {
  1245 	if ( ! isset( $options['slash_zero'] ) ) {
  1581 	if ( ! isset( $options['slash_zero'] ) ) {
  1246 		$options = array( 'slash_zero' => 'remove' );
  1582 		$options = array( 'slash_zero' => 'remove' );
  1247 	}
  1583 	}
  1255 }
  1591 }
  1256 
  1592 
  1257 /**
  1593 /**
  1258  * Strips slashes from in front of quotes.
  1594  * Strips slashes from in front of quotes.
  1259  *
  1595  *
  1260  * This function changes the character sequence \" to just ". It leaves all
  1596  * This function changes the character sequence `\"` to just `"`. It leaves all other
  1261  * other slashes alone. It's really weird, but the quoting from
  1597  * slashes alone. The quoting from `preg_replace(//e)` requires this.
  1262  * preg_replace(//e) seems to require this.
       
  1263  *
  1598  *
  1264  * @since 1.0.0
  1599  * @since 1.0.0
  1265  *
  1600  *
  1266  * @param string $string String to strip slashes
  1601  * @param string $string String to strip slashes from.
  1267  * @return string Fixed string with quoted slashes
  1602  * @return string Fixed string with quoted slashes.
  1268  */
  1603  */
  1269 function wp_kses_stripslashes($string) {
  1604 function wp_kses_stripslashes( $string ) {
  1270 	return preg_replace('%\\\\"%', '"', $string);
  1605 	return preg_replace( '%\\\\"%', '"', $string );
  1271 }
  1606 }
  1272 
  1607 
  1273 /**
  1608 /**
  1274  * Goes through an array and changes the keys to all lower case.
  1609  * Converts the keys of an array to lowercase.
  1275  *
  1610  *
  1276  * @since 1.0.0
  1611  * @since 1.0.0
  1277  *
  1612  *
  1278  * @param array $inarray Unfiltered array
  1613  * @param array $inarray Unfiltered array.
  1279  * @return array Fixed array with all lowercase keys
  1614  * @return array Fixed array with all lowercase keys.
  1280  */
  1615  */
  1281 function wp_kses_array_lc($inarray) {
  1616 function wp_kses_array_lc( $inarray ) {
  1282 	$outarray = array ();
  1617 	$outarray = array();
  1283 
  1618 
  1284 	foreach ( (array) $inarray as $inkey => $inval) {
  1619 	foreach ( (array) $inarray as $inkey => $inval ) {
  1285 		$outkey = strtolower($inkey);
  1620 		$outkey              = strtolower( $inkey );
  1286 		$outarray[$outkey] = array ();
  1621 		$outarray[ $outkey ] = array();
  1287 
  1622 
  1288 		foreach ( (array) $inval as $inkey2 => $inval2) {
  1623 		foreach ( (array) $inval as $inkey2 => $inval2 ) {
  1289 			$outkey2 = strtolower($inkey2);
  1624 			$outkey2                         = strtolower( $inkey2 );
  1290 			$outarray[$outkey][$outkey2] = $inval2;
  1625 			$outarray[ $outkey ][ $outkey2 ] = $inval2;
  1291 		} // foreach $inval
  1626 		}
  1292 	} // foreach $inarray
  1627 	}
  1293 
  1628 
  1294 	return $outarray;
  1629 	return $outarray;
  1295 }
  1630 }
  1296 
  1631 
  1297 /**
  1632 /**
  1298  * Handles parsing errors in wp_kses_hair().
  1633  * Handles parsing errors in `wp_kses_hair()`.
  1299  *
  1634  *
  1300  * The general plan is to remove everything to and including some whitespace,
  1635  * The general plan is to remove everything to and including some whitespace,
  1301  * but it deals with quotes and apostrophes as well.
  1636  * but it deals with quotes and apostrophes as well.
  1302  *
  1637  *
  1303  * @since 1.0.0
  1638  * @since 1.0.0
  1304  *
  1639  *
  1305  * @param string $string
  1640  * @param string $string
  1306  * @return string
  1641  * @return string
  1307  */
  1642  */
  1308 function wp_kses_html_error($string) {
  1643 function wp_kses_html_error( $string ) {
  1309 	return preg_replace('/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string);
  1644 	return preg_replace( '/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string );
  1310 }
  1645 }
  1311 
  1646 
  1312 /**
  1647 /**
  1313  * Sanitizes content from bad protocols and other characters.
  1648  * Sanitizes content from bad protocols and other characters.
  1314  *
  1649  *
  1315  * This function searches for URL protocols at the beginning of $string, while
  1650  * This function searches for URL protocols at the beginning of the string, while
  1316  * handling whitespace and HTML entities.
  1651  * handling whitespace and HTML entities.
  1317  *
  1652  *
  1318  * @since 1.0.0
  1653  * @since 1.0.0
  1319  *
  1654  *
  1320  * @param string $string            Content to check for bad protocols
  1655  * @param string   $string            Content to check for bad protocols.
  1321  * @param string $allowed_protocols Allowed protocols
  1656  * @param string[] $allowed_protocols Array of allowed URL protocols.
  1322  * @return string Sanitized content
  1657  * @return string Sanitized content.
  1323  */
  1658  */
  1324 function wp_kses_bad_protocol_once($string, $allowed_protocols, $count = 1 ) {
  1659 function wp_kses_bad_protocol_once( $string, $allowed_protocols, $count = 1 ) {
       
  1660 	$string  = preg_replace( '/(&#0*58(?![;0-9])|&#x0*3a(?![;a-f0-9]))/i', '$1;', $string );
  1325 	$string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
  1661 	$string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
  1326 	if ( isset($string2[1]) && ! preg_match('%/\?%', $string2[0]) ) {
  1662 	if ( isset( $string2[1] ) && ! preg_match( '%/\?%', $string2[0] ) ) {
  1327 		$string = trim( $string2[1] );
  1663 		$string   = trim( $string2[1] );
  1328 		$protocol = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols );
  1664 		$protocol = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols );
  1329 		if ( 'feed:' == $protocol ) {
  1665 		if ( 'feed:' == $protocol ) {
  1330 			if ( $count > 2 )
  1666 			if ( $count > 2 ) {
  1331 				return '';
  1667 				return '';
       
  1668 			}
  1332 			$string = wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count );
  1669 			$string = wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count );
  1333 			if ( empty( $string ) )
  1670 			if ( empty( $string ) ) {
  1334 				return $string;
  1671 				return $string;
       
  1672 			}
  1335 		}
  1673 		}
  1336 		$string = $protocol . $string;
  1674 		$string = $protocol . $string;
  1337 	}
  1675 	}
  1338 
  1676 
  1339 	return $string;
  1677 	return $string;
  1340 }
  1678 }
  1341 
  1679 
  1342 /**
  1680 /**
  1343  * Callback for wp_kses_bad_protocol_once() regular expression.
  1681  * Callback for `wp_kses_bad_protocol_once()` regular expression.
  1344  *
  1682  *
  1345  * This function processes URL protocols, checks to see if they're in the
  1683  * This function processes URL protocols, checks to see if they're in the
  1346  * whitelist or not, and returns different data depending on the answer.
  1684  * whitelist or not, and returns different data depending on the answer.
  1347  *
  1685  *
  1348  * @access private
  1686  * @access private
       
  1687  * @ignore
  1349  * @since 1.0.0
  1688  * @since 1.0.0
  1350  *
  1689  *
  1351  * @param string $string            URI scheme to check against the whitelist
  1690  * @param string   $string            URI scheme to check against the whitelist.
  1352  * @param string $allowed_protocols Allowed protocols
  1691  * @param string[] $allowed_protocols Array of allowed URL protocols.
  1353  * @return string Sanitized content
  1692  * @return string Sanitized content.
  1354  */
  1693  */
  1355 function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
  1694 function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
  1356 	$string2 = wp_kses_decode_entities($string);
  1695 	$string2 = wp_kses_decode_entities( $string );
  1357 	$string2 = preg_replace('/\s/', '', $string2);
  1696 	$string2 = preg_replace( '/\s/', '', $string2 );
  1358 	$string2 = wp_kses_no_null($string2);
  1697 	$string2 = wp_kses_no_null( $string2 );
  1359 	$string2 = strtolower($string2);
  1698 	$string2 = strtolower( $string2 );
  1360 
  1699 
  1361 	$allowed = false;
  1700 	$allowed = false;
  1362 	foreach ( (array) $allowed_protocols as $one_protocol )
  1701 	foreach ( (array) $allowed_protocols as $one_protocol ) {
  1363 		if ( strtolower($one_protocol) == $string2 ) {
  1702 		if ( strtolower( $one_protocol ) == $string2 ) {
  1364 			$allowed = true;
  1703 			$allowed = true;
  1365 			break;
  1704 			break;
  1366 		}
  1705 		}
  1367 
  1706 	}
  1368 	if ($allowed)
  1707 
       
  1708 	if ( $allowed ) {
  1369 		return "$string2:";
  1709 		return "$string2:";
  1370 	else
  1710 	} else {
  1371 		return '';
  1711 		return '';
       
  1712 	}
  1372 }
  1713 }
  1373 
  1714 
  1374 /**
  1715 /**
  1375  * Converts and fixes HTML entities.
  1716  * Converts and fixes HTML entities.
  1376  *
  1717  *
  1377  * This function normalizes HTML entities. It will convert `AT&T` to the correct
  1718  * This function normalizes HTML entities. It will convert `AT&T` to the correct
  1378  * `AT&amp;T`, `&#00058;` to `&#58;`, `&#XYZZY;` to `&amp;#XYZZY;` and so on.
  1719  * `AT&amp;T`, `&#00058;` to `&#58;`, `&#XYZZY;` to `&amp;#XYZZY;` and so on.
  1379  *
  1720  *
  1380  * @since 1.0.0
  1721  * @since 1.0.0
  1381  *
  1722  *
  1382  * @param string $string Content to normalize entities
  1723  * @param string $string Content to normalize entities.
  1383  * @return string Content with normalized entities
  1724  * @return string Content with normalized entities.
  1384  */
  1725  */
  1385 function wp_kses_normalize_entities($string) {
  1726 function wp_kses_normalize_entities( $string ) {
  1386 	// Disarm all entities by converting & to &amp;
  1727 	// Disarm all entities by converting & to &amp;
  1387 	$string = str_replace('&', '&amp;', $string);
  1728 	$string = str_replace( '&', '&amp;', $string );
  1388 
  1729 
  1389 	// Change back the allowed entities in our entity whitelist
  1730 	// Change back the allowed entities in our entity whitelist
  1390 	$string = preg_replace_callback('/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string);
  1731 	$string = preg_replace_callback( '/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string );
  1391 	$string = preg_replace_callback('/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string);
  1732 	$string = preg_replace_callback( '/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string );
  1392 	$string = preg_replace_callback('/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string);
  1733 	$string = preg_replace_callback( '/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string );
  1393 
  1734 
  1394 	return $string;
  1735 	return $string;
  1395 }
  1736 }
  1396 
  1737 
  1397 /**
  1738 /**
  1398  * Callback for wp_kses_normalize_entities() regular expression.
  1739  * Callback for `wp_kses_normalize_entities()` regular expression.
  1399  *
  1740  *
  1400  * This function only accepts valid named entity references, which are finite,
  1741  * This function only accepts valid named entity references, which are finite,
  1401  * case-sensitive, and highly scrutinized by HTML and XML validators.
  1742  * case-sensitive, and highly scrutinized by HTML and XML validators.
  1402  *
  1743  *
  1403  * @since 3.0.0
  1744  * @since 3.0.0
  1404  *
  1745  *
  1405  * @global array $allowedentitynames
  1746  * @global array $allowedentitynames
  1406  *
  1747  *
  1407  * @param array $matches preg_replace_callback() matches array
  1748  * @param array $matches preg_replace_callback() matches array.
  1408  * @return string Correctly encoded entity
  1749  * @return string Correctly encoded entity.
  1409  */
  1750  */
  1410 function wp_kses_named_entities($matches) {
  1751 function wp_kses_named_entities( $matches ) {
  1411 	global $allowedentitynames;
  1752 	global $allowedentitynames;
  1412 
  1753 
  1413 	if ( empty($matches[1]) )
  1754 	if ( empty( $matches[1] ) ) {
  1414 		return '';
  1755 		return '';
       
  1756 	}
  1415 
  1757 
  1416 	$i = $matches[1];
  1758 	$i = $matches[1];
  1417 	return ( ! in_array( $i, $allowedentitynames ) ) ? "&amp;$i;" : "&$i;";
  1759 	return ( ! in_array( $i, $allowedentitynames ) ) ? "&amp;$i;" : "&$i;";
  1418 }
  1760 }
  1419 
  1761 
  1420 /**
  1762 /**
  1421  * Callback for wp_kses_normalize_entities() regular expression.
  1763  * Callback for `wp_kses_normalize_entities()` regular expression.
  1422  *
  1764  *
  1423  * This function helps wp_kses_normalize_entities() to only accept 16-bit
  1765  * This function helps `wp_kses_normalize_entities()` to only accept 16-bit
  1424  * values and nothing more for `&#number;` entities.
  1766  * values and nothing more for `&#number;` entities.
  1425  *
  1767  *
  1426  * @access private
  1768  * @access private
       
  1769  * @ignore
  1427  * @since 1.0.0
  1770  * @since 1.0.0
  1428  *
  1771  *
  1429  * @param array $matches preg_replace_callback() matches array
  1772  * @param array $matches `preg_replace_callback()` matches array.
  1430  * @return string Correctly encoded entity
  1773  * @return string Correctly encoded entity.
  1431  */
  1774  */
  1432 function wp_kses_normalize_entities2($matches) {
  1775 function wp_kses_normalize_entities2( $matches ) {
  1433 	if ( empty($matches[1]) )
  1776 	if ( empty( $matches[1] ) ) {
  1434 		return '';
  1777 		return '';
       
  1778 	}
  1435 
  1779 
  1436 	$i = $matches[1];
  1780 	$i = $matches[1];
  1437 	if (valid_unicode($i)) {
  1781 	if ( valid_unicode( $i ) ) {
  1438 		$i = str_pad(ltrim($i,'0'), 3, '0', STR_PAD_LEFT);
  1782 		$i = str_pad( ltrim( $i, '0' ), 3, '0', STR_PAD_LEFT );
  1439 		$i = "&#$i;";
  1783 		$i = "&#$i;";
  1440 	} else {
  1784 	} else {
  1441 		$i = "&amp;#$i;";
  1785 		$i = "&amp;#$i;";
  1442 	}
  1786 	}
  1443 
  1787 
  1444 	return $i;
  1788 	return $i;
  1445 }
  1789 }
  1446 
  1790 
  1447 /**
  1791 /**
  1448  * Callback for wp_kses_normalize_entities() for regular expression.
  1792  * Callback for `wp_kses_normalize_entities()` for regular expression.
  1449  *
  1793  *
  1450  * This function helps wp_kses_normalize_entities() to only accept valid Unicode
  1794  * This function helps `wp_kses_normalize_entities()` to only accept valid Unicode
  1451  * numeric entities in hex form.
  1795  * numeric entities in hex form.
  1452  *
  1796  *
  1453  * @since 2.7.0
  1797  * @since 2.7.0
  1454  * @access private
  1798  * @access private
  1455  *
  1799  * @ignore
  1456  * @param array $matches preg_replace_callback() matches array
  1800  *
  1457  * @return string Correctly encoded entity
  1801  * @param array $matches `preg_replace_callback()` matches array.
  1458  */
  1802  * @return string Correctly encoded entity.
  1459 function wp_kses_normalize_entities3($matches) {
  1803  */
  1460 	if ( empty($matches[1]) )
  1804 function wp_kses_normalize_entities3( $matches ) {
       
  1805 	if ( empty( $matches[1] ) ) {
  1461 		return '';
  1806 		return '';
       
  1807 	}
  1462 
  1808 
  1463 	$hexchars = $matches[1];
  1809 	$hexchars = $matches[1];
  1464 	return ( ! valid_unicode( hexdec( $hexchars ) ) ) ? "&amp;#x$hexchars;" : '&#x'.ltrim($hexchars,'0').';';
  1810 	return ( ! valid_unicode( hexdec( $hexchars ) ) ) ? "&amp;#x$hexchars;" : '&#x' . ltrim( $hexchars, '0' ) . ';';
  1465 }
  1811 }
  1466 
  1812 
  1467 /**
  1813 /**
  1468  * Helper function to determine if a Unicode value is valid.
  1814  * Determines if a Unicode codepoint is valid.
  1469  *
  1815  *
  1470  * @since 2.7.0
  1816  * @since 2.7.0
  1471  *
  1817  *
  1472  * @param int $i Unicode value
  1818  * @param int $i Unicode codepoint.
  1473  * @return bool True if the value was a valid Unicode number
  1819  * @return bool Whether or not the codepoint is a valid Unicode codepoint.
  1474  */
  1820  */
  1475 function valid_unicode($i) {
  1821 function valid_unicode( $i ) {
  1476 	return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
  1822 	return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
  1477 			($i >= 0x20 && $i <= 0xd7ff) ||
  1823 			( $i >= 0x20 && $i <= 0xd7ff ) ||
  1478 			($i >= 0xe000 && $i <= 0xfffd) ||
  1824 			( $i >= 0xe000 && $i <= 0xfffd ) ||
  1479 			($i >= 0x10000 && $i <= 0x10ffff) );
  1825 			( $i >= 0x10000 && $i <= 0x10ffff ) );
  1480 }
  1826 }
  1481 
  1827 
  1482 /**
  1828 /**
  1483  * Convert all entities to their character counterparts.
  1829  * Converts all numeric HTML entities to their named counterparts.
  1484  *
  1830  *
  1485  * This function decodes numeric HTML entities (`&#65;` and `&#x41;`).
  1831  * This function decodes numeric HTML entities (`&#65;` and `&#x41;`).
  1486  * It doesn't do anything with other entities like &auml;, but we don't
  1832  * It doesn't do anything with named entities like `&auml;`, but we don't
  1487  * need them in the URL protocol whitelisting system anyway.
  1833  * need them in the URL protocol whitelisting system anyway.
  1488  *
  1834  *
  1489  * @since 1.0.0
  1835  * @since 1.0.0
  1490  *
  1836  *
  1491  * @param string $string Content to change entities
  1837  * @param string $string Content to change entities.
  1492  * @return string Content after decoded entities
  1838  * @return string Content after decoded entities.
  1493  */
  1839  */
  1494 function wp_kses_decode_entities($string) {
  1840 function wp_kses_decode_entities( $string ) {
  1495 	$string = preg_replace_callback('/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string);
  1841 	$string = preg_replace_callback( '/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string );
  1496 	$string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string);
  1842 	$string = preg_replace_callback( '/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string );
  1497 
  1843 
  1498 	return $string;
  1844 	return $string;
  1499 }
  1845 }
  1500 
  1846 
  1501 /**
  1847 /**
  1502  * Regex callback for wp_kses_decode_entities()
  1848  * Regex callback for `wp_kses_decode_entities()`.
  1503  *
  1849  *
  1504  * @since 2.9.0
  1850  * @since 2.9.0
       
  1851  * @access private
       
  1852  * @ignore
  1505  *
  1853  *
  1506  * @param array $match preg match
  1854  * @param array $match preg match
  1507  * @return string
  1855  * @return string
  1508  */
  1856  */
  1509 function _wp_kses_decode_entities_chr( $match ) {
  1857 function _wp_kses_decode_entities_chr( $match ) {
  1510 	return chr( $match[1] );
  1858 	return chr( $match[1] );
  1511 }
  1859 }
  1512 
  1860 
  1513 /**
  1861 /**
  1514  * Regex callback for wp_kses_decode_entities()
  1862  * Regex callback for `wp_kses_decode_entities()`.
  1515  *
  1863  *
  1516  * @since 2.9.0
  1864  * @since 2.9.0
       
  1865  * @access private
       
  1866  * @ignore
  1517  *
  1867  *
  1518  * @param array $match preg match
  1868  * @param array $match preg match
  1519  * @return string
  1869  * @return string
  1520  */
  1870  */
  1521 function _wp_kses_decode_entities_chr_hexdec( $match ) {
  1871 function _wp_kses_decode_entities_chr_hexdec( $match ) {
  1522 	return chr( hexdec( $match[1] ) );
  1872 	return chr( hexdec( $match[1] ) );
  1523 }
  1873 }
  1524 
  1874 
  1525 /**
  1875 /**
  1526  * Sanitize content with allowed HTML Kses rules.
  1876  * Sanitize content with allowed HTML KSES rules.
       
  1877  *
       
  1878  * This function expects slashed data.
  1527  *
  1879  *
  1528  * @since 1.0.0
  1880  * @since 1.0.0
  1529  *
  1881  *
  1530  * @param string $data Content to filter, expected to be escaped with slashes
  1882  * @param string $data Content to filter, expected to be escaped with slashes.
  1531  * @return string Filtered content
  1883  * @return string Filtered content.
  1532  */
  1884  */
  1533 function wp_filter_kses( $data ) {
  1885 function wp_filter_kses( $data ) {
  1534 	return addslashes( wp_kses( stripslashes( $data ), current_filter() ) );
  1886 	return addslashes( wp_kses( stripslashes( $data ), current_filter() ) );
  1535 }
  1887 }
  1536 
  1888 
  1537 /**
  1889 /**
  1538  * Sanitize content with allowed HTML Kses rules.
  1890  * Sanitize content with allowed HTML KSES rules.
       
  1891  *
       
  1892  * This function expects unslashed data.
  1539  *
  1893  *
  1540  * @since 2.9.0
  1894  * @since 2.9.0
  1541  *
  1895  *
  1542  * @param string $data Content to filter, expected to not be escaped
  1896  * @param string $data Content to filter, expected to not be escaped.
  1543  * @return string Filtered content
  1897  * @return string Filtered content.
  1544  */
  1898  */
  1545 function wp_kses_data( $data ) {
  1899 function wp_kses_data( $data ) {
  1546 	return wp_kses( $data, current_filter() );
  1900 	return wp_kses( $data, current_filter() );
  1547 }
  1901 }
  1548 
  1902 
  1549 /**
  1903 /**
  1550  * Sanitize content for allowed HTML tags for post content.
  1904  * Sanitizes content for allowed HTML tags for post content.
  1551  *
  1905  *
  1552  * Post content refers to the page contents of the 'post' type and not $_POST
  1906  * Post content refers to the page contents of the 'post' type and not `$_POST`
  1553  * data from forms.
  1907  * data from forms.
  1554  *
  1908  *
       
  1909  * This function expects slashed data.
       
  1910  *
  1555  * @since 2.0.0
  1911  * @since 2.0.0
  1556  *
  1912  *
  1557  * @param string $data Post content to filter, expected to be escaped with slashes
  1913  * @param string $data Post content to filter, expected to be escaped with slashes.
  1558  * @return string Filtered post content with allowed HTML tags and attributes intact.
  1914  * @return string Filtered post content with allowed HTML tags and attributes intact.
  1559  */
  1915  */
  1560 function wp_filter_post_kses( $data ) {
  1916 function wp_filter_post_kses( $data ) {
  1561 	return addslashes( wp_kses( stripslashes( $data ), 'post' ) );
  1917 	return addslashes( wp_kses( stripslashes( $data ), 'post' ) );
  1562 }
  1918 }
  1563 
  1919 
  1564 /**
  1920 /**
  1565  * Sanitize content for allowed HTML tags for post content.
  1921  * Sanitizes content for allowed HTML tags for post content.
  1566  *
  1922  *
  1567  * Post content refers to the page contents of the 'post' type and not $_POST
  1923  * Post content refers to the page contents of the 'post' type and not `$_POST`
  1568  * data from forms.
  1924  * data from forms.
  1569  *
  1925  *
       
  1926  * This function expects unslashed data.
       
  1927  *
  1570  * @since 2.9.0
  1928  * @since 2.9.0
  1571  *
  1929  *
  1572  * @param string $data Post content to filter
  1930  * @param string $data Post content to filter.
  1573  * @return string Filtered post content with allowed HTML tags and attributes intact.
  1931  * @return string Filtered post content with allowed HTML tags and attributes intact.
  1574  */
  1932  */
  1575 function wp_kses_post( $data ) {
  1933 function wp_kses_post( $data ) {
  1576 	return wp_kses( $data, 'post' );
  1934 	return wp_kses( $data, 'post' );
  1577 }
  1935 }
  1590 function wp_kses_post_deep( $data ) {
  1948 function wp_kses_post_deep( $data ) {
  1591 	return map_deep( $data, 'wp_kses_post' );
  1949 	return map_deep( $data, 'wp_kses_post' );
  1592 }
  1950 }
  1593 
  1951 
  1594 /**
  1952 /**
  1595  * Strips all of the HTML in the content.
  1953  * Strips all HTML from a text string.
       
  1954  *
       
  1955  * This function expects slashed data.
  1596  *
  1956  *
  1597  * @since 2.1.0
  1957  * @since 2.1.0
  1598  *
  1958  *
  1599  * @param string $data Content to strip all HTML from
  1959  * @param string $data Content to strip all HTML from.
  1600  * @return string Filtered content without any HTML
  1960  * @return string Filtered content without any HTML.
  1601  */
  1961  */
  1602 function wp_filter_nohtml_kses( $data ) {
  1962 function wp_filter_nohtml_kses( $data ) {
  1603 	return addslashes( wp_kses( stripslashes( $data ), 'strip' ) );
  1963 	return addslashes( wp_kses( stripslashes( $data ), 'strip' ) );
  1604 }
  1964 }
  1605 
  1965 
  1606 /**
  1966 /**
  1607  * Adds all Kses input form content filters.
  1967  * Adds all KSES input form content filters.
  1608  *
  1968  *
  1609  * All hooks have default priority. The wp_filter_kses() function is added to
  1969  * All hooks have default priority. The `wp_filter_kses()` function is added to
  1610  * the 'pre_comment_content' and 'title_save_pre' hooks.
  1970  * the 'pre_comment_content' and 'title_save_pre' hooks.
  1611  *
  1971  *
  1612  * The wp_filter_post_kses() function is added to the 'content_save_pre',
  1972  * The `wp_filter_post_kses()` function is added to the 'content_save_pre',
  1613  * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
  1973  * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
  1614  *
  1974  *
  1615  * @since 2.0.0
  1975  * @since 2.0.0
  1616  */
  1976  */
  1617 function kses_init_filters() {
  1977 function kses_init_filters() {
  1618 	// Normal filtering
  1978 	// Normal filtering
  1619 	add_filter('title_save_pre', 'wp_filter_kses');
  1979 	add_filter( 'title_save_pre', 'wp_filter_kses' );
  1620 
  1980 
  1621 	// Comment filtering
  1981 	// Comment filtering
  1622 	if ( current_user_can( 'unfiltered_html' ) )
  1982 	if ( current_user_can( 'unfiltered_html' ) ) {
  1623 		add_filter( 'pre_comment_content', 'wp_filter_post_kses' );
  1983 		add_filter( 'pre_comment_content', 'wp_filter_post_kses' );
  1624 	else
  1984 	} else {
  1625 		add_filter( 'pre_comment_content', 'wp_filter_kses' );
  1985 		add_filter( 'pre_comment_content', 'wp_filter_kses' );
       
  1986 	}
  1626 
  1987 
  1627 	// Post filtering
  1988 	// Post filtering
  1628 	add_filter('content_save_pre', 'wp_filter_post_kses');
  1989 	add_filter( 'content_save_pre', 'wp_filter_post_kses' );
  1629 	add_filter('excerpt_save_pre', 'wp_filter_post_kses');
  1990 	add_filter( 'excerpt_save_pre', 'wp_filter_post_kses' );
  1630 	add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
  1991 	add_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
  1631 }
  1992 }
  1632 
  1993 
  1633 /**
  1994 /**
  1634  * Removes all Kses input form content filters.
  1995  * Removes all KSES input form content filters.
  1635  *
  1996  *
  1636  * A quick procedural method to removing all of the filters that kses uses for
  1997  * A quick procedural method to removing all of the filters that KSES uses for
  1637  * content in WordPress Loop.
  1998  * content in WordPress Loop.
  1638  *
  1999  *
  1639  * Does not remove the kses_init() function from {@see 'init'} hook (priority is
  2000  * Does not remove the `kses_init()` function from {@see 'init'} hook (priority is
  1640  * default). Also does not remove kses_init() function from {@see 'set_current_user'}
  2001  * default). Also does not remove `kses_init()` function from {@see 'set_current_user'}
  1641  * hook (priority is also default).
  2002  * hook (priority is also default).
  1642  *
  2003  *
  1643  * @since 2.0.6
  2004  * @since 2.0.6
  1644  */
  2005  */
  1645 function kses_remove_filters() {
  2006 function kses_remove_filters() {
  1646 	// Normal filtering
  2007 	// Normal filtering
  1647 	remove_filter('title_save_pre', 'wp_filter_kses');
  2008 	remove_filter( 'title_save_pre', 'wp_filter_kses' );
  1648 
  2009 
  1649 	// Comment filtering
  2010 	// Comment filtering
  1650 	remove_filter( 'pre_comment_content', 'wp_filter_post_kses' );
  2011 	remove_filter( 'pre_comment_content', 'wp_filter_post_kses' );
  1651 	remove_filter( 'pre_comment_content', 'wp_filter_kses' );
  2012 	remove_filter( 'pre_comment_content', 'wp_filter_kses' );
  1652 
  2013 
  1653 	// Post filtering
  2014 	// Post filtering
  1654 	remove_filter('content_save_pre', 'wp_filter_post_kses');
  2015 	remove_filter( 'content_save_pre', 'wp_filter_post_kses' );
  1655 	remove_filter('excerpt_save_pre', 'wp_filter_post_kses');
  2016 	remove_filter( 'excerpt_save_pre', 'wp_filter_post_kses' );
  1656 	remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
  2017 	remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
  1657 }
  2018 }
  1658 
  2019 
  1659 /**
  2020 /**
  1660  * Sets up most of the Kses filters for input form content.
  2021  * Sets up most of the KSES filters for input form content.
  1661  *
  2022  *
  1662  * If you remove the kses_init() function from {@see 'init'} hook and
  2023  * First removes all of the KSES filters in case the current user does not need
  1663  * {@see 'set_current_user'} (priority is default), then none of the Kses filter hooks
  2024  * to have KSES filter the content. If the user does not have `unfiltered_html`
  1664  * will be added.
  2025  * capability, then KSES filters are added.
  1665  *
       
  1666  * First removes all of the Kses filters in case the current user does not need
       
  1667  * to have Kses filter the content. If the user does not have unfiltered_html
       
  1668  * capability, then Kses filters are added.
       
  1669  *
  2026  *
  1670  * @since 2.0.0
  2027  * @since 2.0.0
  1671  */
  2028  */
  1672 function kses_init() {
  2029 function kses_init() {
  1673 	kses_remove_filters();
  2030 	kses_remove_filters();
  1676 		kses_init_filters();
  2033 		kses_init_filters();
  1677 	}
  2034 	}
  1678 }
  2035 }
  1679 
  2036 
  1680 /**
  2037 /**
  1681  * Inline CSS filter
  2038  * Filters an inline style attribute and removes disallowed rules.
  1682  *
  2039  *
  1683  * @since 2.8.1
  2040  * @since 2.8.1
  1684  *
  2041  *
  1685  * @param string $css        A string of CSS rules.
  2042  * @param string $css        A string of CSS rules.
  1686  * @param string $deprecated Not used.
  2043  * @param string $deprecated Not used.
  1687  * @return string            Filtered string of CSS rules.
  2044  * @return string Filtered string of CSS rules.
  1688  */
  2045  */
  1689 function safecss_filter_attr( $css, $deprecated = '' ) {
  2046 function safecss_filter_attr( $css, $deprecated = '' ) {
  1690 	if ( !empty( $deprecated ) )
  2047 	if ( ! empty( $deprecated ) ) {
  1691 		_deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
  2048 		_deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
  1692 
  2049 	}
  1693 	$css = wp_kses_no_null($css);
  2050 
  1694 	$css = str_replace(array("\n","\r","\t"), '', $css);
  2051 	$css = wp_kses_no_null( $css );
  1695 
  2052 	$css = str_replace( array( "\n", "\r", "\t" ), '', $css );
  1696 	if ( preg_match( '%[\\\\(&=}]|/\*%', $css ) ) // remove any inline css containing \ ( & } = or comments
  2053 
  1697 		return '';
  2054 	$allowed_protocols = wp_allowed_protocols();
  1698 
  2055 
  1699 	$css_array = explode( ';', trim( $css ) );
  2056 	$css_array = explode( ';', trim( $css ) );
  1700 
  2057 
  1701 	/**
  2058 	/**
  1702 	 * Filters list of allowed CSS attributes.
  2059 	 * Filters list of allowed CSS attributes.
  1703 	 *
  2060 	 *
  1704 	 * @since 2.8.1
  2061 	 * @since 2.8.1
  1705 	 * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`.
  2062 	 * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`.
  1706 	 * @since 4.6.0 Added support for `list-style-type`.
  2063 	 * @since 4.6.0 Added support for `list-style-type`.
       
  2064 	 * @since 5.0.0 Added support for `background-image`.
       
  2065 	 * @since 5.1.0 Added support for `text-transform`.
       
  2066 	 * @since 5.2.0 Added support for `background-position` and `grid-template-columns`
  1707 	 *
  2067 	 *
  1708 	 * @param array $attr List of allowed CSS attributes.
  2068 	 * @param string[] $attr Array of allowed CSS attributes.
  1709 	 */
  2069 	 */
  1710 	$allowed_attr = apply_filters( 'safe_style_css', array(
  2070 	$allowed_attr = apply_filters(
       
  2071 		'safe_style_css',
       
  2072 		array(
       
  2073 			'background',
       
  2074 			'background-color',
       
  2075 			'background-image',
       
  2076 			'background-position',
       
  2077 
       
  2078 			'border',
       
  2079 			'border-width',
       
  2080 			'border-color',
       
  2081 			'border-style',
       
  2082 			'border-right',
       
  2083 			'border-right-color',
       
  2084 			'border-right-style',
       
  2085 			'border-right-width',
       
  2086 			'border-bottom',
       
  2087 			'border-bottom-color',
       
  2088 			'border-bottom-style',
       
  2089 			'border-bottom-width',
       
  2090 			'border-left',
       
  2091 			'border-left-color',
       
  2092 			'border-left-style',
       
  2093 			'border-left-width',
       
  2094 			'border-top',
       
  2095 			'border-top-color',
       
  2096 			'border-top-style',
       
  2097 			'border-top-width',
       
  2098 
       
  2099 			'border-spacing',
       
  2100 			'border-collapse',
       
  2101 			'caption-side',
       
  2102 
       
  2103 			'color',
       
  2104 			'font',
       
  2105 			'font-family',
       
  2106 			'font-size',
       
  2107 			'font-style',
       
  2108 			'font-variant',
       
  2109 			'font-weight',
       
  2110 			'letter-spacing',
       
  2111 			'line-height',
       
  2112 			'text-align',
       
  2113 			'text-decoration',
       
  2114 			'text-indent',
       
  2115 			'text-transform',
       
  2116 
       
  2117 			'height',
       
  2118 			'min-height',
       
  2119 			'max-height',
       
  2120 
       
  2121 			'width',
       
  2122 			'min-width',
       
  2123 			'max-width',
       
  2124 
       
  2125 			'margin',
       
  2126 			'margin-right',
       
  2127 			'margin-bottom',
       
  2128 			'margin-left',
       
  2129 			'margin-top',
       
  2130 
       
  2131 			'padding',
       
  2132 			'padding-right',
       
  2133 			'padding-bottom',
       
  2134 			'padding-left',
       
  2135 			'padding-top',
       
  2136 
       
  2137 			'clear',
       
  2138 			'cursor',
       
  2139 			'direction',
       
  2140 			'float',
       
  2141 			'overflow',
       
  2142 			'vertical-align',
       
  2143 			'list-style-type',
       
  2144 			'grid-template-columns',
       
  2145 		)
       
  2146 	);
       
  2147 
       
  2148 	/*
       
  2149 	 * CSS attributes that accept URL data types.
       
  2150 	 *
       
  2151 	 * This is in accordance to the CSS spec and unrelated to
       
  2152 	 * the sub-set of supported attributes above.
       
  2153 	 *
       
  2154 	 * See: https://developer.mozilla.org/en-US/docs/Web/CSS/url
       
  2155 	 */
       
  2156 	$css_url_data_types = array(
  1711 		'background',
  2157 		'background',
  1712 		'background-color',
  2158 		'background-image',
  1713 
  2159 
  1714 		'border',
       
  1715 		'border-width',
       
  1716 		'border-color',
       
  1717 		'border-style',
       
  1718 		'border-right',
       
  1719 		'border-right-color',
       
  1720 		'border-right-style',
       
  1721 		'border-right-width',
       
  1722 		'border-bottom',
       
  1723 		'border-bottom-color',
       
  1724 		'border-bottom-style',
       
  1725 		'border-bottom-width',
       
  1726 		'border-left',
       
  1727 		'border-left-color',
       
  1728 		'border-left-style',
       
  1729 		'border-left-width',
       
  1730 		'border-top',
       
  1731 		'border-top-color',
       
  1732 		'border-top-style',
       
  1733 		'border-top-width',
       
  1734 
       
  1735 		'border-spacing',
       
  1736 		'border-collapse',
       
  1737 		'caption-side',
       
  1738 
       
  1739 		'color',
       
  1740 		'font',
       
  1741 		'font-family',
       
  1742 		'font-size',
       
  1743 		'font-style',
       
  1744 		'font-variant',
       
  1745 		'font-weight',
       
  1746 		'letter-spacing',
       
  1747 		'line-height',
       
  1748 		'text-decoration',
       
  1749 		'text-indent',
       
  1750 		'text-align',
       
  1751 
       
  1752 		'height',
       
  1753 		'min-height',
       
  1754 		'max-height',
       
  1755 
       
  1756 		'width',
       
  1757 		'min-width',
       
  1758 		'max-width',
       
  1759 
       
  1760 		'margin',
       
  1761 		'margin-right',
       
  1762 		'margin-bottom',
       
  1763 		'margin-left',
       
  1764 		'margin-top',
       
  1765 
       
  1766 		'padding',
       
  1767 		'padding-right',
       
  1768 		'padding-bottom',
       
  1769 		'padding-left',
       
  1770 		'padding-top',
       
  1771 
       
  1772 		'clear',
       
  1773 		'cursor',
  2160 		'cursor',
  1774 		'direction',
  2161 
  1775 		'float',
  2162 		'list-style',
  1776 		'overflow',
  2163 		'list-style-image',
  1777 		'vertical-align',
  2164 	);
  1778 		'list-style-type',
  2165 
  1779 	) );
  2166 	if ( empty( $allowed_attr ) ) {
  1780 
       
  1781 	if ( empty($allowed_attr) )
       
  1782 		return $css;
  2167 		return $css;
       
  2168 	}
  1783 
  2169 
  1784 	$css = '';
  2170 	$css = '';
  1785 	foreach ( $css_array as $css_item ) {
  2171 	foreach ( $css_array as $css_item ) {
  1786 		if ( $css_item == '' )
  2172 		if ( $css_item == '' ) {
  1787 			continue;
  2173 			continue;
  1788 		$css_item = trim( $css_item );
  2174 		}
  1789 		$found = false;
  2175 
       
  2176 		$css_item        = trim( $css_item );
       
  2177 		$css_test_string = $css_item;
       
  2178 		$found           = false;
       
  2179 		$url_attr        = false;
       
  2180 
  1790 		if ( strpos( $css_item, ':' ) === false ) {
  2181 		if ( strpos( $css_item, ':' ) === false ) {
  1791 			$found = true;
  2182 			$found = true;
  1792 		} else {
  2183 		} else {
  1793 			$parts = explode( ':', $css_item );
  2184 			$parts        = explode( ':', $css_item, 2 );
  1794 			if ( in_array( trim( $parts[0] ), $allowed_attr ) )
  2185 			$css_selector = trim( $parts[0] );
  1795 				$found = true;
  2186 
       
  2187 			if ( in_array( $css_selector, $allowed_attr, true ) ) {
       
  2188 				$found    = true;
       
  2189 				$url_attr = in_array( $css_selector, $css_url_data_types, true );
       
  2190 			}
  1796 		}
  2191 		}
  1797 		if ( $found ) {
  2192 
  1798 			if( $css != '' )
  2193 		if ( $found && $url_attr ) {
       
  2194 			// Simplified: matches the sequence `url(*)`.
       
  2195 			preg_match_all( '/url\([^)]+\)/', $parts[1], $url_matches );
       
  2196 
       
  2197 			foreach ( $url_matches[0] as $url_match ) {
       
  2198 				// Clean up the URL from each of the matches above.
       
  2199 				preg_match( '/^url\(\s*([\'\"]?)(.*)(\g1)\s*\)$/', $url_match, $url_pieces );
       
  2200 
       
  2201 				if ( empty( $url_pieces[2] ) ) {
       
  2202 					$found = false;
       
  2203 					break;
       
  2204 				}
       
  2205 
       
  2206 				$url = trim( $url_pieces[2] );
       
  2207 
       
  2208 				if ( empty( $url ) || $url !== wp_kses_bad_protocol( $url, $allowed_protocols ) ) {
       
  2209 					$found = false;
       
  2210 					break;
       
  2211 				} else {
       
  2212 					// Remove the whole `url(*)` bit that was matched above from the CSS.
       
  2213 					$css_test_string = str_replace( $url_match, '', $css_test_string );
       
  2214 				}
       
  2215 			}
       
  2216 		}
       
  2217 
       
  2218 		// Remove any CSS containing containing \ ( & } = or comments, except for url() useage checked above.
       
  2219 		if ( $found && ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ) ) {
       
  2220 			if ( $css != '' ) {
  1799 				$css .= ';';
  2221 				$css .= ';';
       
  2222 			}
       
  2223 
  1800 			$css .= $css_item;
  2224 			$css .= $css_item;
  1801 		}
  2225 		}
  1802 	}
  2226 	}
  1803 
  2227 
  1804 	return $css;
  2228 	return $css;
  1806 
  2230 
  1807 /**
  2231 /**
  1808  * Helper function to add global attributes to a tag in the allowed html list.
  2232  * Helper function to add global attributes to a tag in the allowed html list.
  1809  *
  2233  *
  1810  * @since 3.5.0
  2234  * @since 3.5.0
       
  2235  * @since 5.0.0 Add support for `data-*` wildcard attributes.
  1811  * @access private
  2236  * @access private
       
  2237  * @ignore
  1812  *
  2238  *
  1813  * @param array $value An array of attributes.
  2239  * @param array $value An array of attributes.
  1814  * @return array The array of attributes with global attributes added.
  2240  * @return array The array of attributes with global attributes added.
  1815  */
  2241  */
  1816 function _wp_add_global_attributes( $value ) {
  2242 function _wp_add_global_attributes( $value ) {
  1817 	$global_attributes = array(
  2243 	$global_attributes = array(
  1818 		'class' => true,
  2244 		'aria-describedby' => true,
  1819 		'id' => true,
  2245 		'aria-details'     => true,
  1820 		'style' => true,
  2246 		'aria-label'       => true,
  1821 		'title' => true,
  2247 		'aria-labelledby'  => true,
  1822 		'role' => true,
  2248 		'aria-hidden'      => true,
       
  2249 		'class'            => true,
       
  2250 		'id'               => true,
       
  2251 		'style'            => true,
       
  2252 		'title'            => true,
       
  2253 		'role'             => true,
       
  2254 		'data-*'           => true,
  1823 	);
  2255 	);
  1824 
  2256 
  1825 	if ( true === $value )
  2257 	if ( true === $value ) {
  1826 		$value = array();
  2258 		$value = array();
  1827 
  2259 	}
  1828 	if ( is_array( $value ) )
  2260 
       
  2261 	if ( is_array( $value ) ) {
  1829 		return array_merge( $value, $global_attributes );
  2262 		return array_merge( $value, $global_attributes );
       
  2263 	}
  1830 
  2264 
  1831 	return $value;
  2265 	return $value;
  1832 }
  2266 }