Tests_Kses   F
last analyzed

Complexity

Total Complexity 84

Size/Duplication

Total Lines 704
Duplicated Lines 12.78 %

Coupling/Cohesion

Components 0
Dependencies 1

Importance

Changes 0
Metric Value
dl 90
loc 704
rs 1.0434
c 0
b 0
f 0
wmc 84
lcom 0
cbo 1

25 Methods

Rating   Name   Duplication   Size   Complexity  
B test_feed_links() 0 33 1
C test_wp_kses_bad_protocol() 0 61 13
D test_hackers_attacks() 0 130 43
A _wp_kses_allowed_html_filter() 0 6 2
A test_wp_kses_allowed_html() 0 47 2
A test_hyphenated_tag() 0 13 1
A test_ctrl_removal() 0 5 1
A test_slash_zero_removal() 0 5 1
A test_hair_parse() 0 3 1
B data_hair_parse() 0 36 1
A test_attr_parse() 0 3 1
A data_attr_parse() 0 48 1
A test_one_attr() 0 3 1
A data_one_attr() 0 64 1
A test_bdo() 0 7 1
A test_ol_reversed() 0 7 1
A test_wp_kses_attr_no_attributes_allowed_with_empty_array() 0 6 1
A test_wp_kses_attr_no_attributes_allowed_with_true() 0 6 1
A test_wp_kses_attr_single_attribute_is_allowed() 0 6 1
A test_wp_filter_post_kses_address() 5 19 2
A test_wp_filter_post_kses_a() 5 21 2
A test_wp_filter_post_kses_abbr() 5 16 2
A test_wp_kses_normalize_entities() 11 11 1
B data_ctrl_removal() 24 24 1
B data_slash_zero_removal() 40 40 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Tests_Kses often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Tests_Kses, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Some simple test cases for KSES post content filtering
4
 *
5
 * @group formatting
6
 * @group kses
7
 */
8
class Tests_Kses extends WP_UnitTestCase {
9
10
	/**
11
	 * @ticket 20210
12
	 */
13
	function test_wp_filter_post_kses_address() {
14
		global $allowedposttags;
15
16
		$attributes = array(
17
			'class' => 'classname',
18
			'id' => 'id',
19
			'style' => 'color: red;',
20
			'style' => 'color: red',
21
			'style' => 'color: red; text-align:center',
22
			'style' => 'color: red; text-align:center;',
23
			'title' => 'title',
24
		);
25
26 View Code Duplication
		foreach ( $attributes as $name => $value ) {
27
			$string = "<address $name='$value'>1 WordPress Avenue, The Internet.</address>";
28
			$expect_string = "<address $name='" . str_replace( '; ', ';', trim( $value, ';' ) ) . "'>1 WordPress Avenue, The Internet.</address>";
29
			$this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
30
		}
31
	}
32
33
	/**
34
	 * @ticket 20210
35
	 */
36
	function test_wp_filter_post_kses_a() {
37
		global $allowedposttags;
38
39
		$attributes = array(
40
			'class' => 'classname',
41
			'id' => 'id',
42
			'style' => 'color: red;',
43
			'title' => 'title',
44
			'href' => 'http://example.com',
45
			'rel' => 'related',
46
			'rev' => 'revision',
47
			'name' => 'name',
48
			'target' => '_blank',
49
		);
50
51 View Code Duplication
		foreach ( $attributes as $name => $value ) {
52
			$string = "<a $name='$value'>I link this</a>";
53
			$expect_string = "<a $name='" . trim( $value, ';' ) . "'>I link this</a>";
54
			$this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
55
		}
56
	}
57
58
	/**
59
	 * @ticket 20210
60
	 */
61
	function test_wp_filter_post_kses_abbr() {
62
		global $allowedposttags;
63
64
		$attributes = array(
65
			'class' => 'classname',
66
			'id' => 'id',
67
			'style' => 'color: red;',
68
			'title' => 'title',
69
		);
70
71 View Code Duplication
		foreach ( $attributes as $name => $value ) {
72
			$string = "<abbr $name='$value'>WP</abbr>";
73
			$expect_string = "<abbr $name='" . trim( $value, ';' ) . "'>WP</abbr>";
74
			$this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
75
		}
76
	}
77
78
	function test_feed_links() {
79
		global $allowedposttags;
80
81
		$content = <<<EOF
82
<a href="feed:javascript:alert(1)">CLICK ME</a>
83
<a href="feed:javascript:feed:alert(1)">CLICK ME</a>
84
<a href="feed:feed:javascript:alert(1)">CLICK ME</a>
85
<a href="javascript:feed:alert(1)">CLICK ME</a>
86
<a href="javascript:feed:javascript:alert(1)">CLICK ME</a>
87
<a href="feed:feed:feed:javascript:alert(1)">CLICK ME</a>
88
<a href="feed:feed:feed:feed:javascript:alert(1)">CLICK ME</a>
89
<a href="feed:feed:feed:feed:feed:javascript:alert(1)">CLICK ME</a>
90
<a href="feed:javascript:feed:javascript:feed:javascript:alert(1)">CLICK ME</a>
91
<a href="feed:javascript:feed:javascript:feed:javascript:feed:javascript:feed:javascript:alert(1)">CLICK ME</a>
92
<a href="feed:feed:feed:http:alert(1)">CLICK ME</a>
93
EOF;
94
95
		$expected = <<<EOF
96
<a href="feed:alert(1)">CLICK ME</a>
97
<a href="feed:feed:alert(1)">CLICK ME</a>
98
<a href="feed:feed:alert(1)">CLICK ME</a>
99
<a href="feed:alert(1)">CLICK ME</a>
100
<a href="feed:alert(1)">CLICK ME</a>
101
<a href="">CLICK ME</a>
102
<a href="">CLICK ME</a>
103
<a href="">CLICK ME</a>
104
<a href="">CLICK ME</a>
105
<a href="">CLICK ME</a>
106
<a href="">CLICK ME</a>
107
EOF;
108
109
		$this->assertEquals( $expected, wp_kses( $content, $allowedposttags ) );
110
	}
111
112
	function test_wp_kses_bad_protocol() {
113
		$bad = array(
114
			'dummy:alert(1)',
115
			'javascript:alert(1)',
116
			'JaVaScRiPt:alert(1)',
117
			'javascript:alert(1);',
118
			'javascript&#58;alert(1);',
119
			'javascript&#0058;alert(1);',
120
			'javascript&#0000058alert(1);',
121
			'javascript&#x3A;alert(1);',
122
			'javascript&#X3A;alert(1);',
123
			'javascript&#X3a;alert(1);',
124
			'javascript&#x3a;alert(1);',
125
			'javascript&#x003a;alert(1);',
126
			'&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29',
127
			'jav	ascript:alert(1);',
128
			'jav&#x09;ascript:alert(1);',
129
			'jav&#x0A;ascript:alert(1);',
130
			'jav&#x0D;ascript:alert(1);',
131
			' &#14;  javascript:alert(1);',
132
			'javascript:javascript:alert(1);',
133
			'javascript&#58;javascript:alert(1);',
134
			'javascript&#0000058javascript:alert(1);',
135
			'javascript:javascript&#58;alert(1);',
136
			'javascript:javascript&#0000058alert(1);',
137
			'javascript&#0000058alert(1)//?:',
138
			'feed:javascript:alert(1)',
139
			'feed:javascript:feed:javascript:feed:javascript:alert(1)',
140
		);
141
		foreach ( $bad as $k => $x ) {
142
			$result = wp_kses_bad_protocol( wp_kses_normalize_entities( $x ), wp_allowed_protocols() );
143
			if ( ! empty( $result ) && $result != 'alert(1);' && $result != 'alert(1)' ) {
144
				switch ( $k ) {
145
					case 6: $this->assertEquals( 'javascript&amp;#0000058alert(1);', $result ); break;
146
					case 12:
147
						$this->assertEquals( str_replace( '&', '&amp;', $x ), $result );
148
						break;
149
					case 22: $this->assertEquals( 'javascript&amp;#0000058alert(1);', $result ); break;
150
					case 23: $this->assertEquals( 'javascript&amp;#0000058alert(1)//?:', $result ); break;
151
					case 24: $this->assertEquals( 'feed:alert(1)', $result ); break;
152
					default: $this->fail( "wp_kses_bad_protocol failed on $x. Result: $result" );
153
				}
154
			}
155
		}
156
157
		$safe = array(
158
			'dummy:alert(1)',
159
			'HTTP://example.org/',
160
			'http://example.org/',
161
			'http&#58;//example.org/',
162
			'http&#x3A;//example.org/',
163
			'https://example.org',
164
			'http://example.org/wp-admin/post.php?post=2&amp;action=edit',
165
			'http://example.org/index.php?test=&#039;blah&#039;',
166
		);
167
		foreach ( $safe as $x ) {
168
			$result = wp_kses_bad_protocol( wp_kses_normalize_entities( $x ), array( 'http', 'https', 'dummy' ) );
169
			if ( $result != $x && $result != 'http://example.org/' )
170
				$this->fail( "wp_kses_bad_protocol incorrectly blocked $x" );
171
		}
172
	}
173
174
	public function test_hackers_attacks() {
175
		$xss = simplexml_load_file( DIR_TESTDATA . '/formatting/xssAttacks.xml' );
176
		foreach ( $xss->attack as $attack ) {
177
			if ( in_array( $attack->name, array( 'IMG Embedded commands 2', 'US-ASCII encoding', 'OBJECT w/Flash 2', 'Character Encoding Example' ) ) )
178
				continue;
179
180
			$code = (string) $attack->code;
181
182
			if ( $code == 'See Below' )
183
				continue;
184
185
			if ( substr( $code, 0, 4 ) == 'perl' ) {
186
				$pos = strpos( $code, '"' ) + 1;
187
				$code = substr( $code, $pos, strrpos($code, '"') - $pos );
188
				$code = str_replace( '\0', "\0", $code );
189
			}
190
191
			$result = trim( wp_kses_data( $code ) );
192
193
			if ( $result == '' || $result == 'XSS' || $result == 'alert("XSS");' || $result == "alert('XSS');" )
194
				continue;
195
196
			switch ( $attack->name ) {
197
				case 'XSS Locator':
198
					$this->assertEquals('\';alert(String.fromCharCode(88,83,83))//\\\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\\";alert(String.fromCharCode(88,83,83))//--&gt;"&gt;\'&gt;alert(String.fromCharCode(88,83,83))=&amp;{}', $result);
199
					break;
200
				case 'XSS Quick Test':
201
					$this->assertEquals('\'\';!--"=&amp;{()}', $result);
202
					break;
203
				case 'SCRIPT w/Alert()':
204
					$this->assertEquals( "alert('XSS')", $result );
205
					break;
206
				case 'SCRIPT w/Char Code':
207
					$this->assertEquals('alert(String.fromCharCode(88,83,83))', $result);
208
					break;
209
				case 'IMG STYLE w/expression':
210
					$this->assertEquals('exp/*', $result);
211
					break;
212
				case 'List-style-image':
213
					$this->assertEquals('li {list-style-image: url("javascript:alert(\'XSS\')");}XSS', $result);
214
					break;
215
				case 'STYLE':
216
					$this->assertEquals( "alert('XSS');", $result);
217
					break;
218
				case 'STYLE w/background-image':
219
					$this->assertEquals('.XSS{background-image:url("javascript:alert(\'XSS\')");}<A></A>', $result);
220
					break;
221
				case 'STYLE w/background':
222
					$this->assertEquals('BODY{background:url("javascript:alert(\'XSS\')")}', $result);
223
					break;
224
				case 'Remote Stylesheet 2':
225
					$this->assertEquals( "@import'http://ha.ckers.org/xss.css';", $result );
226
					break;
227
				case 'Remote Stylesheet 3':
228
					$this->assertEquals( '&lt;META HTTP-EQUIV=&quot;Link&quot; Content=&quot;; REL=stylesheet"&gt;', $result );
229
					break;
230
				case 'Remote Stylesheet 4':
231
					$this->assertEquals('BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}', $result);
232
					break;
233
				case 'XML data island w/CDATA':
234
					$this->assertEquals( "&lt;![CDATA[]]&gt;", $result );
235
					break;
236
				case 'XML data island w/comment':
237
					$this->assertEquals( "<I><B>&lt;IMG SRC=&quot;javas<!-- -->cript:alert('XSS')\"&gt;</B></I>", $result );
238
					break;
239
				case 'XML HTML+TIME':
240
					$this->assertEquals( '&lt;t:set attributeName=&quot;innerHTML&quot; to=&quot;XSSalert(\'XSS\')"&gt;', $result );
241
					break;
242
				case 'Commented-out Block':
243
					$this->assertEquals( "<!--[if gte IE 4]&gt;-->\nalert('XSS');", $result );
244
					break;
245
				case 'Cookie Manipulation':
246
					$this->assertEquals( '&lt;META HTTP-EQUIV=&quot;Set-Cookie&quot; Content=&quot;USERID=alert(\'XSS\')"&gt;', $result );
247
					break;
248
				case 'SSI':
249
					$this->assertEquals( '&lt;!--#exec cmd=&quot;/bin/echo &#039;<!--#exec cmd="/bin/echo \'=http://ha.ckers.org/xss.js&gt;\'"-->', $result );
250
					break;
251
				case 'PHP':
252
					$this->assertEquals( '&lt;? echo(&#039;alert("XSS")\'); ?&gt;', $result );
253
					break;
254
				case 'UTF-7 Encoding':
255
					$this->assertEquals( '+ADw-SCRIPT+AD4-alert(\'XSS\');+ADw-/SCRIPT+AD4-', $result );
256
					break;
257
				case 'Escaping JavaScript escapes':
258
					$this->assertEquals('\";alert(\'XSS\');//', $result);
259
					break;
260
				case 'STYLE w/broken up JavaScript':
261
					$this->assertEquals( '@im\port\'\ja\vasc\ript:alert("XSS")\';', $result );
262
					break;
263
				case 'Null Chars 2':
264
					$this->assertEquals( '&amp;alert("XSS")', $result );
265
					break;
266
				case 'No Closing Script Tag':
267
					$this->assertEquals( '&lt;SCRIPT SRC=http://ha.ckers.org/xss.js', $result );
268
					break;
269
				case 'Half-Open HTML/JavaScript':
270
					$this->assertEquals( '&lt;IMG SRC=&quot;javascript:alert(&#039;XSS&#039;)&quot;', $result );
271
					break;
272
				case 'Double open angle brackets':
273
					$this->assertEquals( '&lt;IFRAME SRC=http://ha.ckers.org/scriptlet.html &lt;', $result );
274
					break;
275
				case 'Extraneous Open Brackets':
276
					$this->assertEquals( '&lt;alert("XSS");//&lt;', $result );
277
					break;
278
				case 'Malformed IMG Tags':
279
					$this->assertEquals('alert("XSS")"&gt;', $result);
280
					break;
281
				case 'No Quotes/Semicolons':
282
					$this->assertEquals( "a=/XSS/\nalert(a.source)", $result );
283
					break;
284
				case 'Evade Regex Filter 1':
285
					$this->assertEquals( '" SRC="http://ha.ckers.org/xss.js"&gt;', $result );
286
					break;
287
				case 'Evade Regex Filter 4':
288
					$this->assertEquals( '\'" SRC="http://ha.ckers.org/xss.js"&gt;', $result );
289
					break;
290
				case 'Evade Regex Filter 5':
291
					$this->assertEquals( '` SRC="http://ha.ckers.org/xss.js"&gt;', $result );
292
					break;
293
				case 'Filter Evasion 1':
294
					$this->assertEquals( 'document.write("&lt;SCRI&quot;);PT SRC="http://ha.ckers.org/xss.js"&gt;', $result );
295
					break;
296
				case 'Filter Evasion 2':
297
					$this->assertEquals( '\'&gt;" SRC="http://ha.ckers.org/xss.js"&gt;', $result );
298
					break;
299
				default:
300
					$this->fail( 'KSES failed on ' . $attack->name . ': ' . $result );
301
			}
302
		}
303
	}
304
305
	function _wp_kses_allowed_html_filter( $html, $context ) {
306
		if ( 'post' == $context )
307
			return array( 'a' => array( 'href' => true ) );
308
		else
309
			return array( 'a' => array( 'href' => false ) );
310
	}
311
312
	/**
313
	 * @ticket 20210
314
	 */
315
	public function test_wp_kses_allowed_html() {
316
		global $allowedposttags, $allowedtags, $allowedentitynames;
317
318
		$this->assertEquals( $allowedposttags, wp_kses_allowed_html( 'post' ) );
319
320
		$tags = wp_kses_allowed_html( 'post' ) ;
321
322
		foreach ( $tags as $tag ) {
323
			$this->assertTrue( $tag['class'] );
324
			$this->assertTrue( $tag['id'] );
325
			$this->assertTrue( $tag['style'] );
326
			$this->assertTrue( $tag['title'] );
327
		}
328
329
		$this->assertEquals( $allowedtags, wp_kses_allowed_html( 'data' ) );
330
		$this->assertEquals( $allowedtags, wp_kses_allowed_html( '' ) );
331
		$this->assertEquals( $allowedtags, wp_kses_allowed_html() );
332
333
		$tags = wp_kses_allowed_html( 'user_description' );
334
		$this->assertTrue( $tags['a']['rel'] );
335
336
		$tags = wp_kses_allowed_html();
337
		$this->assertFalse( isset( $tags['a']['rel'] ) );
338
339
		$this->assertEquals( array(), wp_kses_allowed_html( 'strip' ) );
340
341
		$custom_tags = array(
342
			'a' => array(
343
				'href' => true,
344
				'rel' => true,
345
				'rev' => true,
346
				'name' => true,
347
				'target' => true,
348
			),
349
		);
350
351
		$this->assertEquals( $custom_tags, wp_kses_allowed_html( $custom_tags ) );
352
353
		add_filter( 'wp_kses_allowed_html', array( $this, '_wp_kses_allowed_html_filter' ), 10, 2 );
354
355
		$this->assertEquals( array( 'a' => array( 'href' => true ) ), wp_kses_allowed_html( 'post' ) );
356
		$this->assertEquals( array( 'a' => array( 'href' => false ) ), wp_kses_allowed_html( 'data' ) );
357
358
		remove_filter( 'wp_kses_allowed_html', array( $this, '_wp_kses_allowed_html_filter' ) );
359
		$this->assertEquals( $allowedposttags, wp_kses_allowed_html( 'post' ) );
360
		$this->assertEquals( $allowedtags, wp_kses_allowed_html( 'data' ) );
361
	}
362
363
	function test_hyphenated_tag() {
364
		$string = "<hyphenated-tag attribute=\"value\" otherattribute=\"value2\">Alot of hyphens.</hyphenated-tag>";
365
		$custom_tags = array(
366
			'hyphenated-tag' => array(
367
				'attribute' => true,
368
			),
369
		);
370
		$expect_stripped_string = 'Alot of hyphens.';
371
372
		$expect_valid_string = "<hyphenated-tag attribute=\"value\">Alot of hyphens.</hyphenated-tag>";
373
		$this->assertEquals( $expect_stripped_string, wp_kses_post( $string ) );
374
		$this->assertEquals( $expect_valid_string, wp_kses( $string, $custom_tags ) );
375
	}
376
377
	/**
378
	 * @ticket 26290
379
	 */
380 View Code Duplication
	public function test_wp_kses_normalize_entities() {
381
		$this->assertEquals( '&spades;', wp_kses_normalize_entities( '&spades;' ) );
382
383
		$this->assertEquals( '&sup1;', wp_kses_normalize_entities( '&sup1;' ) );
384
		$this->assertEquals( '&sup2;', wp_kses_normalize_entities( '&sup2;' ) );
385
		$this->assertEquals( '&sup3;', wp_kses_normalize_entities( '&sup3;' ) );
386
		$this->assertEquals( '&frac14;', wp_kses_normalize_entities( '&frac14;' ) );
387
		$this->assertEquals( '&frac12;', wp_kses_normalize_entities( '&frac12;' ) );
388
		$this->assertEquals( '&frac34;', wp_kses_normalize_entities( '&frac34;' ) );
389
		$this->assertEquals( '&there4;', wp_kses_normalize_entities( '&there4;' ) );
390
	}
391
392
	/**
393
	 * Test removal of invalid binary data for HTML.
394
	 *
395
	 * @ticket 28506
396
	 * @dataProvider data_ctrl_removal
397
	 */
398
	function test_ctrl_removal( $input, $output ) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
399
		global $allowedposttags;
400
401
		return $this->assertEquals( $output, wp_kses( $input, $allowedposttags ) );
402
	}
403
404 View Code Duplication
	function data_ctrl_removal() {
405
		return array(
406
			array(
407
				"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\X1C\x1D\x1E\x1F",
408
				'',
409
			),
410
			array(
411
				"\x00h\x01e\x02l\x03l\x04o\x05 \x06w\x07o\x08r\x0Bl\x0Cd\x0E.\x0F \x10W\x11O\x12R\x13D\x14P\x15R\x16E\x17S\x18S\x19 \x1AK\x1BS\X1CE\x1DS\x1E.\x1F/",
412
				'hello world. WORDPRESS KSES./',
413
			),
414
			array(
415
				"\x1F\x1E\x1D\x1C\x1B\x1A\x19\x18\x17\x16\x15\x14\x13\x12\x11\x10\x0F\x0E\x0C\x0B\x08\x07\x06\x05\x04\X03\x02\x01\x00",
416
				'',
417
			),
418
			array(
419
				"\x1Fh\x1Ee\x1Dl\x1Cl\x1Bo\x1A \x19w\x18o\x17r\x16l\x15d\x14.\x13 \x12W\x11O\x10R\x0FD\x0EP\x0CR\x0BE\x08S\x07S\x06 \x05K\x04S\X03E\x02S\x01.\x00/",
420
				'hello world. WORDPRESS KSES./',
421
			),
422
			array(
423
				"\t\r\n word \n\r\t",
424
				"\t\r\n word \n\r\t",
425
			),
426
		);
427
	}
428
429
	/**
430
	 * Test removal of '\0' strings.
431
	 *
432
	 * @ticket 28699
433
	 * @dataProvider data_slash_zero_removal
434
	 */
435
	function test_slash_zero_removal( $input, $output ) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
436
		global $allowedposttags;
437
438
		return $this->assertEquals( $output, wp_kses( $input, $allowedposttags ) );
439
	}
440
441 View Code Duplication
	function data_slash_zero_removal() {
442
		return array(
443
			array(
444
				'This \\0 should be no big deal.',
445
				'This \\0 should be no big deal.',
446
			),
447
			array(
448
				'<div>This \\0 should be no big deal.</div>',
449
				'<div>This \\0 should be no big deal.</div>',
450
			),
451
			array(
452
				'<div align="\\0left">This should be no big deal.</div>',
453
				'<div align="\\0left">This should be no big deal.</div>',
454
			),
455
			array(
456
				'This <div style="float:\\0left"> is more of a concern.',
457
				'This <div style="float:left"> is more of a concern.',
458
			),
459
			array(
460
				'This <div style="float:\\0\\0left"> is more of a concern.',
461
				'This <div style="float:left"> is more of a concern.',
462
			),
463
			array(
464
				'This <div style="float:\\\\00left"> is more of a concern.',
465
				'This <div style="float:left"> is more of a concern.',
466
			),
467
			array(
468
				'This <div style="float:\\\\\\\\0000left"> is more of a concern.',
469
				'This <div style="float:left"> is more of a concern.',
470
			),
471
			array(
472
				'This <div style="float:\\0000left"> is more of a concern.',
473
				'This <div style="float:left"> is more of a concern.',
474
			),
475
			array(
476
				'<style type="text/css">div {background-image:\\0}</style>',
477
				'div {background-image:\\0}',
478
			),
479
		);
480
	}
481
482
	/**
483
	 * Test new function wp_kses_hair_parse().
484
	 *
485
	 * @dataProvider data_hair_parse
486
	 */
487
	function test_hair_parse( $input, $output ) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
488
		return $this->assertEquals( $output, wp_kses_hair_parse( $input ) );
489
	}
490
491
	function data_hair_parse() {
492
		return array(
493
			array(
494
				'title="hello" href="#" id="my_id" ',
495
				array( 'title="hello" ', 'href="#" ', 'id="my_id" ' ),
496
			),
497
			array(
498
				'[shortcode attr="value"] href="http://www.google.com/"title="moo"disabled',
499
				array( '[shortcode attr="value"] ', 'href="http://www.google.com/"', 'title="moo"', 'disabled' ),
500
			),
501
			array(
502
				'',
503
				array(),
504
			),
505
			array(
506
				'a',
507
				array( 'a' ),
508
			),
509
			array(
510
				'title="hello"disabled href=# id=\'my_id\'',
511
				array( 'title="hello"', 'disabled ', 'href=# ', "id='my_id'" ),
512
			),
513
			array(
514
				'     ', // Calling function is expected to strip leading whitespace.
515
				false,
516
			),
517
			array(
518
				'abcd=abcd"abcd"',
519
				false,
520
			),
521
			array(
522
				"array[1]='z'z'z'z",
523
				false,
524
			),
525
		);
526
	}
527
528
	/**
529
	 * Test new function wp_kses_attr_parse().
530
	 *
531
	 * @dataProvider data_attr_parse
532
	 */
533
	function test_attr_parse( $input, $output ) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
534
		return $this->assertEquals( $output, wp_kses_attr_parse( $input ) );
535
	}
536
537
	function data_attr_parse() {
538
		return array(
539
			array(
540
				'<a title="hello" href="#" id="my_id" >',
541
				array( '<a ', 'title="hello" ', 'href="#" ', 'id="my_id" ', '>' ),
542
			),
543
			array(
544
				'<a [shortcode attr="value"] href="http://www.google.com/"title="moo"disabled>',
545
				array( '<a ', '[shortcode attr="value"] ', 'href="http://www.google.com/"', 'title="moo"', 'disabled', '>' ),
546
			),
547
			array(
548
				'',
549
				false,
550
			),
551
			array(
552
				'a',
553
				false,
554
			),
555
			array(
556
				'<a>',
557
				array( '<a', '>' ),
558
			),
559
			array(
560
				'<a%%&&**>',
561
				false,
562
			),
563
			array(
564
				'<a title="hello"disabled href=# id=\'my_id\'>',
565
				array( '<a ', 'title="hello"', 'disabled ', 'href=# ', "id='my_id'", ">" ),
566
			),
567
			array(
568
				'<a     >',
569
				array( '<a     ', '>' ),
570
			),
571
			array(
572
				'<a abcd=abcd"abcd">',
573
				false,
574
			),
575
			array(
576
				"<a array[1]='z'z'z'z>",
577
				false,
578
			),
579
			array(
580
				'<img title="hello" src="#" id="my_id" />',
581
				array( '<img ', 'title="hello" ', 'src="#" ', 'id="my_id"', ' />' ),
582
			),
583
		);
584
	}
585
586
	/**
587
	 * Test new function wp_kses_one_attr().
588
	 *
589
	 * @dataProvider data_one_attr
590
	 */
591
	function test_one_attr( $element, $input, $output ) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
592
		return $this->assertEquals( $output, wp_kses_one_attr( $input, $element ) );
593
	}
594
595
	function data_one_attr() {
596
		return array(
597
			array(
598
				'a',
599
				' title="hello" ',
600
				' title="hello" ',
601
			),
602
			array(
603
				'a',
604
				'title  =  "hello"',
605
				'title="hello"',
606
			),
607
			array(
608
				'a',
609
				"title='hello'",
610
				"title='hello'",
611
			),
612
			array(
613
				'a',
614
				'title=hello',
615
				'title="hello"',
616
			),
617
			array(
618
				'a',
619
				'href="javascript:alert(1)"',
620
				'href="alert(1)"',
621
			),
622
			array(
623
				'a',
624
				'style ="style "',
625
				'style="style"',
626
			),
627
			array(
628
				'a',
629
				'style="style "',
630
				'style="style"',
631
			),
632
			array(
633
				'a',
634
				'style ="style ="',
635
				'',
636
			),
637
			array(
638
				'img',
639
				'src="mypic.jpg"',
640
				'src="mypic.jpg"',
641
			),
642
			array(
643
				'img',
644
				'onerror=alert(1)',
645
				'',
646
			),
647
			array(
648
				'img',
649
				'title=>',
650
				'title="&gt;"',
651
			),
652
			array(
653
				'img',
654
				'title="&garbage";"',
655
				'title="&amp;garbage&quot;;"',
656
			),
657
		);
658
	}
659
660
	/**
661
	 * @ticket 34063
662
	 */
663
	function test_bdo() {
664
		global $allowedposttags;
665
666
		$input = '<p>This is <bdo dir="rtl">a BDO tag</bdo>. Weird, <bdo dir="ltr">right?</bdo></p>';
667
668
		$this->assertEquals( $input, wp_kses( $input, $allowedposttags ) );
669
	}
670
671
	/**
672
	 * @ticket 35079
673
	 */
674
	function test_ol_reversed() {
675
		global $allowedposttags;
676
677
		$input = '<ol reversed="reversed"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ol>';
678
679
		$this->assertEquals( $input, wp_kses( $input, $allowedposttags ) );
680
	}
681
682
	/**
683
	 * @ticket 40680
684
	 */
685
	function test_wp_kses_attr_no_attributes_allowed_with_empty_array() {
686
		$element = 'foo';
687
		$attribute = 'title="foo" class="bar"';
688
689
		$this->assertEquals( "<{$element}>", wp_kses_attr( $element, $attribute, array( 'foo' => array() ), array() ) );
690
	}
691
692
	/**
693
	 * @ticket 40680
694
	 */
695
	function test_wp_kses_attr_no_attributes_allowed_with_true() {
696
		$element = 'foo';
697
		$attribute = 'title="foo" class="bar"';
698
699
		$this->assertEquals( "<{$element}>", wp_kses_attr( $element, $attribute, array( 'foo' => true ), array() ) );
700
	}
701
702
	/**
703
	 * @ticket 40680
704
	 */
705
	function test_wp_kses_attr_single_attribute_is_allowed() {
706
		$element = 'foo';
707
		$attribute = 'title="foo" class="bar"';
708
709
		$this->assertEquals( "<{$element} title=\"foo\">", wp_kses_attr( $element, $attribute, array( 'foo' => array( 'title' => true ) ), array() ) );
710
	}
711
}
712