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 '>'; |
1043 return '>'; |
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 |
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. |
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( '/(�*58(?![;0-9])|�*3a(?![;a-f0-9]))/i', '$1;', $string ); |
1325 $string2 = preg_split( '/:|�*58;|�*3a;/i', $string, 2 ); |
1661 $string2 = preg_split( '/:|�*58;|�*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&T`, `:` to `:`, `&#XYZZY;` to `&#XYZZY;` and so on. |
1719 * `AT&T`, `:` to `:`, `&#XYZZY;` to `&#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 & |
1727 // Disarm all entities by converting & to & |
1387 $string = str_replace('&', '&', $string); |
1728 $string = str_replace( '&', '&', $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('/&([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string); |
1731 $string = preg_replace_callback( '/&([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string ); |
1391 $string = preg_replace_callback('/&#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string); |
1732 $string = preg_replace_callback( '/&#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string ); |
1392 $string = preg_replace_callback('/&#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string); |
1733 $string = preg_replace_callback( '/&#[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 ) ) ? "&$i;" : "&$i;"; |
1759 return ( ! in_array( $i, $allowedentitynames ) ) ? "&$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 = "&#$i;"; |
1785 $i = "&#$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 ) ) ) ? "&#x$hexchars;" : '&#x'.ltrim($hexchars,'0').';'; |
1810 return ( ! valid_unicode( hexdec( $hexchars ) ) ) ? "&#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 (`A` and `A`). |
1831 * This function decodes numeric HTML entities (`A` and `A`). |
1486 * It doesn't do anything with other entities like ä, but we don't |
1832 * It doesn't do anything with named entities like `ä`, 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; |