| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 3 | //
 | 
        
           |  |  | 4 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 5 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 6 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 7 | // (at your option) any later version.
 | 
        
           |  |  | 8 | //
 | 
        
           |  |  | 9 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 12 | // GNU General Public License for more details.
 | 
        
           |  |  | 13 | //
 | 
        
           |  |  | 14 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 15 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | namespace core;
 | 
        
           |  |  | 18 |   | 
        
           |  |  | 19 | /**
 | 
        
           |  |  | 20 |  * HTMLPurifier test case
 | 
        
           |  |  | 21 |  *
 | 
        
           |  |  | 22 |  * @package    core
 | 
        
           |  |  | 23 |  * @category   test
 | 
        
           |  |  | 24 |  * @copyright  2012 Petr Skoda {@link http://skodak.org}
 | 
        
           |  |  | 25 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 26 |  */
 | 
        
           |  |  | 27 | class htmlpurifier_test extends \basic_testcase {
 | 
        
           |  |  | 28 |   | 
        
           |  |  | 29 |     /**
 | 
        
           |  |  | 30 |      * Verify _blank target is allowed.
 | 
        
           |  |  | 31 |      */
 | 
        
           | 11 | efrain | 32 |     public function test_allow_blank_target(): void {
 | 
        
           | 1 | efrain | 33 |         // See MDL-52651 for an explanation as to why the rel="noreferrer" attribute is expected here.
 | 
        
           |  |  | 34 |         // Also note we do not need to test links with an existing rel attribute as the HTML Purifier is configured to remove
 | 
        
           |  |  | 35 |         // the rel attribute.
 | 
        
           |  |  | 36 |         $text = '<a href="http://moodle.org" target="_blank">Some link</a>';
 | 
        
           |  |  | 37 |         $expected = '<a href="http://moodle.org" target="_blank" rel="noreferrer noopener">Some link</a>';
 | 
        
           |  |  | 38 |         $result = format_text($text, FORMAT_HTML);
 | 
        
           |  |  | 39 |         $this->assertSame($expected, $result);
 | 
        
           |  |  | 40 |   | 
        
           |  |  | 41 |         $result = format_text('<a href="http://moodle.org" target="some">Some link</a>', FORMAT_HTML);
 | 
        
           |  |  | 42 |         $this->assertSame('<a href="http://moodle.org">Some link</a>', $result);
 | 
        
           |  |  | 43 |     }
 | 
        
           |  |  | 44 |   | 
        
           |  |  | 45 |     /**
 | 
        
           |  |  | 46 |      * Verify our nolink tag accepted.
 | 
        
           |  |  | 47 |      */
 | 
        
           | 11 | efrain | 48 |     public function test_nolink(): void {
 | 
        
           | 1 | efrain | 49 |         // We can not use format text because nolink changes result.
 | 
        
           |  |  | 50 |         $text = '<nolink><div>no filters</div></nolink>';
 | 
        
           |  |  | 51 |         $result = purify_html($text, array());
 | 
        
           |  |  | 52 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 53 |   | 
        
           |  |  | 54 |         $text = '<nolink>xxx<em>xx</em><div>xxx</div></nolink>';
 | 
        
           |  |  | 55 |         $result = purify_html($text, array());
 | 
        
           |  |  | 56 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 57 |   | 
        
           |  |  | 58 |         // Ensure nolink doesn't force open tags to be closed, so can be virtually everywhere.
 | 
        
           |  |  | 59 |         $text = '<p><nolink><div>no filters</div></nolink></p>';
 | 
        
           |  |  | 60 |         $result = purify_html($text, array());
 | 
        
           |  |  | 61 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 62 |     }
 | 
        
           |  |  | 63 |   | 
        
           |  |  | 64 |     /**
 | 
        
           |  |  | 65 |      * Verify our tex tag accepted.
 | 
        
           |  |  | 66 |      */
 | 
        
           | 11 | efrain | 67 |     public function test_tex(): void {
 | 
        
           | 1 | efrain | 68 |         $text = '<tex>a+b=c</tex>';
 | 
        
           |  |  | 69 |         $result = purify_html($text, array());
 | 
        
           |  |  | 70 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 71 |     }
 | 
        
           |  |  | 72 |   | 
        
           |  |  | 73 |     /**
 | 
        
           |  |  | 74 |      * Verify our algebra tag accepted.
 | 
        
           |  |  | 75 |      */
 | 
        
           | 11 | efrain | 76 |     public function test_algebra(): void {
 | 
        
           | 1 | efrain | 77 |         $text = '<algebra>a+b=c</algebra>';
 | 
        
           |  |  | 78 |         $result = purify_html($text, array());
 | 
        
           |  |  | 79 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 80 |     }
 | 
        
           |  |  | 81 |   | 
        
           |  |  | 82 |     /**
 | 
        
           |  |  | 83 |      * Verify our hacky multilang works.
 | 
        
           |  |  | 84 |      */
 | 
        
           | 11 | efrain | 85 |     public function test_multilang(): void {
 | 
        
           | 1 | efrain | 86 |         $text = '<lang lang="en">hmmm</lang><lang lang="anything">hm</lang>';
 | 
        
           |  |  | 87 |         $result = purify_html($text, array());
 | 
        
           |  |  | 88 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 89 |   | 
        
           |  |  | 90 |         $text = '<span lang="en" class="multilang">hmmm</span><span lang="anything" class="multilang">hm</span>';
 | 
        
           |  |  | 91 |         $result = purify_html($text, array());
 | 
        
           |  |  | 92 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 93 |   | 
        
           |  |  | 94 |         $text = '<span lang="en">hmmm</span>';
 | 
        
           |  |  | 95 |         $result = purify_html($text, array());
 | 
        
           |  |  | 96 |         $this->assertNotSame($text, $result);
 | 
        
           |  |  | 97 |   | 
        
           |  |  | 98 |         // Keep standard lang tags.
 | 
        
           |  |  | 99 |   | 
        
           |  |  | 100 |         $text = '<span lang="de_DU" class="multilang">asas</span>';
 | 
        
           |  |  | 101 |         $result = purify_html($text, array());
 | 
        
           |  |  | 102 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 103 |   | 
        
           |  |  | 104 |         $text = '<lang lang="de_DU">xxxxxx</lang>';
 | 
        
           |  |  | 105 |         $result = purify_html($text, array());
 | 
        
           |  |  | 106 |         $this->assertSame($text, $result);
 | 
        
           |  |  | 107 |     }
 | 
        
           |  |  | 108 |   | 
        
           |  |  | 109 |     /**
 | 
        
           |  |  | 110 |      * Tests the 'allowid' option for format_text.
 | 
        
           |  |  | 111 |      */
 | 
        
           | 11 | efrain | 112 |     public function test_format_text_allowid(): void {
 | 
        
           | 1 | efrain | 113 |         // Start off by not allowing ids (default).
 | 
        
           |  |  | 114 |         $options = [
 | 
        
           |  |  | 115 |             'allowid' => false,
 | 
        
           |  |  | 116 |         ];
 | 
        
           |  |  | 117 |         $result = format_text('<div id="example">Frog</div>', FORMAT_HTML, $options);
 | 
        
           |  |  | 118 |         $this->assertSame('<div>Frog</div>', $result);
 | 
        
           |  |  | 119 |   | 
        
           |  |  | 120 |         // Now allow ids.
 | 
        
           |  |  | 121 |         $options['allowid'] = true;
 | 
        
           |  |  | 122 |         $result = format_text('<div id="example">Frog</div>', FORMAT_HTML, $options);
 | 
        
           |  |  | 123 |         $this->assertSame('<div id="example">Frog</div>', $result);
 | 
        
           |  |  | 124 |     }
 | 
        
           |  |  | 125 |   | 
        
           | 11 | efrain | 126 |     public function test_allowobjectembed(): void {
 | 
        
           | 1 | efrain | 127 |         global $CFG;
 | 
        
           |  |  | 128 |   | 
        
           |  |  | 129 |         $this->assertSame('0', $CFG->allowobjectembed);
 | 
        
           |  |  | 130 |   | 
        
           |  |  | 131 |         $text = '<object width="425" height="350">
 | 
        
           |  |  | 132 | <param name="movie" value="http://www.youtube.com/v/AyPzM5WK8ys" />
 | 
        
           |  |  | 133 | <param name="wmode" value="transparent" />
 | 
        
           |  |  | 134 | <embed src="http://www.youtube.com/v/AyPzM5WK8ys" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350" />
 | 
        
           |  |  | 135 | </object>hmmm';
 | 
        
           |  |  | 136 |         $result = purify_html($text, array());
 | 
        
           |  |  | 137 |         $this->assertSame('hmmm', trim($result));
 | 
        
           |  |  | 138 |   | 
        
           |  |  | 139 |         $CFG->allowobjectembed = '1';
 | 
        
           |  |  | 140 |   | 
        
           |  |  | 141 |         $expected = '<object width="425" height="350" data="http://www.youtube.com/v/AyPzM5WK8ys" type="application/x-shockwave-flash">
 | 
        
           |  |  | 142 | <param name="allowScriptAccess" value="never" />
 | 
        
           |  |  | 143 | <param name="allowNetworking" value="internal" />
 | 
        
           |  |  | 144 | <param name="movie" value="http://www.youtube.com/v/AyPzM5WK8ys" />
 | 
        
           |  |  | 145 | <param name="wmode" value="transparent" />
 | 
        
           |  |  | 146 | <embed src="http://www.youtube.com/v/AyPzM5WK8ys" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350" allowscriptaccess="never" allownetworking="internal" />
 | 
        
           |  |  | 147 | </object>hmmm';
 | 
        
           |  |  | 148 |         $result = purify_html($text, array());
 | 
        
           |  |  | 149 |         $this->assertSame(str_replace("\n", '', $expected), str_replace("\n", '', $result));
 | 
        
           |  |  | 150 |   | 
        
           |  |  | 151 |         $CFG->allowobjectembed = '0';
 | 
        
           |  |  | 152 |   | 
        
           |  |  | 153 |         $result = purify_html($text, array());
 | 
        
           |  |  | 154 |         $this->assertSame('hmmm', trim($result));
 | 
        
           |  |  | 155 |     }
 | 
        
           |  |  | 156 |   | 
        
           |  |  | 157 |     /**
 | 
        
           |  |  | 158 |      * Test if linebreaks kept unchanged.
 | 
        
           |  |  | 159 |      */
 | 
        
           | 11 | efrain | 160 |     public function test_line_breaking(): void {
 | 
        
           | 1 | efrain | 161 |         $text = "\n\raa\rsss\nsss\r";
 | 
        
           |  |  | 162 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 163 |     }
 | 
        
           |  |  | 164 |   | 
        
           |  |  | 165 |     /**
 | 
        
           |  |  | 166 |      * Test fixing of strict problems.
 | 
        
           |  |  | 167 |      */
 | 
        
           | 11 | efrain | 168 |     public function test_tidy(): void {
 | 
        
           | 1 | efrain | 169 |         $text = "<p>xx";
 | 
        
           |  |  | 170 |         $this->assertSame('<p>xx</p>', purify_html($text));
 | 
        
           |  |  | 171 |   | 
        
           |  |  | 172 |         $text = "<P>xx</P>";
 | 
        
           |  |  | 173 |         $this->assertSame('<p>xx</p>', purify_html($text));
 | 
        
           |  |  | 174 |   | 
        
           |  |  | 175 |         $text = "xx<br>";
 | 
        
           |  |  | 176 |         $this->assertSame('xx<br />', purify_html($text));
 | 
        
           |  |  | 177 |     }
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 |     /**
 | 
        
           |  |  | 180 |      * Test nesting - this used to cause problems in earlier versions.
 | 
        
           |  |  | 181 |      */
 | 
        
           | 11 | efrain | 182 |     public function test_nested_lists(): void {
 | 
        
           | 1 | efrain | 183 |         $text = "<ul><li>One<ul><li>Two</li></ul></li><li>Three</li></ul>";
 | 
        
           |  |  | 184 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 185 |     }
 | 
        
           |  |  | 186 |   | 
        
           |  |  | 187 |     /**
 | 
        
           |  |  | 188 |      * Test that XSS protection works, complete smoke tests are in htmlpurifier itself.
 | 
        
           |  |  | 189 |      */
 | 
        
           | 11 | efrain | 190 |     public function test_cleaning_nastiness(): void {
 | 
        
           | 1 | efrain | 191 |         $text = "x<SCRIPT>alert('XSS')</SCRIPT>x";
 | 
        
           |  |  | 192 |         $this->assertSame('xx', purify_html($text));
 | 
        
           |  |  | 193 |   | 
        
           |  |  | 194 |         $text = '<DIV STYLE="background-image:url(javascript:alert(\'XSS\'))">xx</DIV>';
 | 
        
           |  |  | 195 |         $this->assertSame('<div>xx</div>', purify_html($text));
 | 
        
           |  |  | 196 |   | 
        
           |  |  | 197 |         $text = '<DIV STYLE="width:expression(alert(\'XSS\'));">xx</DIV>';
 | 
        
           |  |  | 198 |         $this->assertSame('<div>xx</div>', purify_html($text));
 | 
        
           |  |  | 199 |   | 
        
           |  |  | 200 |         $text = 'x<IFRAME SRC="javascript:alert(\'XSS\');"></IFRAME>x';
 | 
        
           |  |  | 201 |         $this->assertSame('xx', purify_html($text));
 | 
        
           |  |  | 202 |   | 
        
           |  |  | 203 |         $text = 'x<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>x';
 | 
        
           |  |  | 204 |         $this->assertSame('xx', purify_html($text));
 | 
        
           |  |  | 205 |   | 
        
           |  |  | 206 |         $text = 'x<EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED>x';
 | 
        
           |  |  | 207 |         $this->assertSame('xx', purify_html($text));
 | 
        
           |  |  | 208 |   | 
        
           |  |  | 209 |         $text = 'x<form></form>x';
 | 
        
           |  |  | 210 |         $this->assertSame('xx', purify_html($text));
 | 
        
           |  |  | 211 |     }
 | 
        
           |  |  | 212 |   | 
        
           |  |  | 213 |     /**
 | 
        
           |  |  | 214 |      * Test internal function used for clean_text() speedup.
 | 
        
           |  |  | 215 |      */
 | 
        
           | 11 | efrain | 216 |     public function test_is_purify_html_necessary(): void {
 | 
        
           | 1 | efrain | 217 |         // First our shortcuts.
 | 
        
           |  |  | 218 |         $text = "";
 | 
        
           |  |  | 219 |         $this->assertFalse(is_purify_html_necessary($text));
 | 
        
           |  |  | 220 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 221 |   | 
        
           |  |  | 222 |         $text = "666";
 | 
        
           |  |  | 223 |         $this->assertFalse(is_purify_html_necessary($text));
 | 
        
           |  |  | 224 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 225 |   | 
        
           |  |  | 226 |         $text = "abc\ndef \" ' ";
 | 
        
           |  |  | 227 |         $this->assertFalse(is_purify_html_necessary($text));
 | 
        
           |  |  | 228 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 229 |   | 
        
           |  |  | 230 |         $text = "abc\n<p>def</p>efg<p>hij</p>";
 | 
        
           |  |  | 231 |         $this->assertFalse(is_purify_html_necessary($text));
 | 
        
           |  |  | 232 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 233 |   | 
        
           |  |  | 234 |         $text = "<br />abc\n<p>def<em>efg</em><strong>hi<br />j</strong></p>";
 | 
        
           |  |  | 235 |         $this->assertFalse(is_purify_html_necessary($text));
 | 
        
           |  |  | 236 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 237 |   | 
        
           |  |  | 238 |         // Now failures.
 | 
        
           |  |  | 239 |         $text = " ";
 | 
        
           |  |  | 240 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 241 |   | 
        
           |  |  | 242 |         $text = "Gin & Tonic";
 | 
        
           |  |  | 243 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 244 |   | 
        
           |  |  | 245 |         $text = "Gin > Tonic";
 | 
        
           |  |  | 246 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 247 |   | 
        
           |  |  | 248 |         $text = "Gin < Tonic";
 | 
        
           |  |  | 249 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 250 |   | 
        
           |  |  | 251 |         $text = "<div>abc</div>";
 | 
        
           |  |  | 252 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 253 |   | 
        
           |  |  | 254 |         $text = "<span>abc</span>";
 | 
        
           |  |  | 255 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 256 |   | 
        
           |  |  | 257 |         $text = "<br>abc";
 | 
        
           |  |  | 258 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 259 |   | 
        
           |  |  | 260 |         $text = "<p class='xxx'>abc</p>";
 | 
        
           |  |  | 261 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 262 |   | 
        
           |  |  | 263 |         $text = "<p>abc<em></p></em>";
 | 
        
           |  |  | 264 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 265 |   | 
        
           |  |  | 266 |         $text = "<p>abc";
 | 
        
           |  |  | 267 |         $this->assertTrue(is_purify_html_necessary($text));
 | 
        
           |  |  | 268 |     }
 | 
        
           |  |  | 269 |   | 
        
           | 11 | efrain | 270 |     public function test_allowed_schemes(): void {
 | 
        
           | 1 | efrain | 271 |         // First standard schemas.
 | 
        
           |  |  | 272 |         $text = '<a href="http://www.example.com/course/view.php?id=5">link</a>';
 | 
        
           |  |  | 273 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 274 |   | 
        
           |  |  | 275 |         $text = '<a href="https://www.example.com/course/view.php?id=5">link</a>';
 | 
        
           |  |  | 276 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |         $text = '<a href="ftp://user@ftp.example.com/some/file.txt">link</a>';
 | 
        
           |  |  | 279 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 280 |   | 
        
           |  |  | 281 |         $text = '<a href="nntp://example.com/group/123">link</a>';
 | 
        
           |  |  | 282 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 283 |   | 
        
           |  |  | 284 |         $text = '<a href="news:groupname">link</a>';
 | 
        
           |  |  | 285 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 286 |   | 
        
           |  |  | 287 |         $text = '<a href="mailto:user@example.com">link</a>';
 | 
        
           |  |  | 288 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 289 |   | 
        
           |  |  | 290 |         // Extra schemes allowed in moodle.
 | 
        
           |  |  | 291 |         $text = '<a href="irc://irc.example.com/3213?pass">link</a>';
 | 
        
           |  |  | 292 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 293 |   | 
        
           |  |  | 294 |         $text = '<a href="rtsp://www.example.com/movie.mov">link</a>';
 | 
        
           |  |  | 295 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 296 |   | 
        
           |  |  | 297 |         $text = '<a href="rtmp://www.example.com/video.f4v">link</a>';
 | 
        
           |  |  | 298 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 299 |   | 
        
           |  |  | 300 |         $text = '<a href="teamspeak://speak.example.com/?par=val?par2=val2">link</a>';
 | 
        
           |  |  | 301 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 302 |   | 
        
           |  |  | 303 |         $text = '<a href="gopher://gopher.example.com/resource">link</a>';
 | 
        
           |  |  | 304 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 305 |   | 
        
           |  |  | 306 |         $text = '<a href="mms://www.example.com/movie.mms">link</a>';
 | 
        
           |  |  | 307 |         $this->assertSame($text, purify_html($text));
 | 
        
           |  |  | 308 |   | 
        
           |  |  | 309 |         // Now some borked or dangerous schemes.
 | 
        
           |  |  | 310 |         $text = '<a href="javascript://www.example.com">link</a>';
 | 
        
           |  |  | 311 |         $this->assertSame('<a>link</a>', purify_html($text));
 | 
        
           |  |  | 312 |   | 
        
           |  |  | 313 |         $text = '<a href="hmmm://www.example.com">link</a>';
 | 
        
           |  |  | 314 |         $this->assertSame('<a>link</a>', purify_html($text));
 | 
        
           |  |  | 315 |     }
 | 
        
           |  |  | 316 |   | 
        
           |  |  | 317 |     /**
 | 
        
           |  |  | 318 |      * Test non-ascii domain names
 | 
        
           |  |  | 319 |      */
 | 
        
           | 11 | efrain | 320 |     public function test_idn(): void {
 | 
        
           | 1 | efrain | 321 |   | 
        
           |  |  | 322 |         // Example of domain that gives the same result in IDNA2003 and IDNA2008 .
 | 
        
           |  |  | 323 |         $text = '<a href="http://правительство.рф">правительство.рф</a>';
 | 
        
           |  |  | 324 |         $expected = '<a href="http://xn--80aealotwbjpid2k.xn--p1ai">правительство.рф</a>';
 | 
        
           |  |  | 325 |         $this->assertSame($expected, purify_html($text));
 | 
        
           |  |  | 326 |   | 
        
           |  |  | 327 |         // Examples of deviations from http://www.unicode.org/reports/tr46/#Table_Deviation_Characters .
 | 
        
           |  |  | 328 |         $text = '<a href="http://teßt.de">teßt.de</a>';
 | 
        
           |  |  | 329 |         $expected = '<a href="http://xn--tet-6ka.de">teßt.de</a>';
 | 
        
           |  |  | 330 |         $this->assertSame($expected, purify_html($text));
 | 
        
           |  |  | 331 |   | 
        
           |  |  | 332 |         $text = '<a href="http://βόλος.com">http://βόλος.com</a>';
 | 
        
           |  |  | 333 |         $expected = '<a href="http://xn--nxasmm1c.com">http://βόλος.com</a>';
 | 
        
           |  |  | 334 |         $this->assertSame($expected, purify_html($text));
 | 
        
           |  |  | 335 |   | 
        
           |  |  | 336 |         $text = '<a href="http://نامهای.com">http://نامهای.com</a>';
 | 
        
           |  |  | 337 |         $expected = '<a href="http://xn--mgba3gch31f060k.com">http://نامهای.com</a>';
 | 
        
           |  |  | 338 |         $this->assertSame($expected, purify_html($text));
 | 
        
           |  |  | 339 |     }
 | 
        
           |  |  | 340 |   | 
        
           |  |  | 341 |     /**
 | 
        
           |  |  | 342 |      * Tests media tags.
 | 
        
           |  |  | 343 |      *
 | 
        
           |  |  | 344 |      * @dataProvider media_tags_provider
 | 
        
           |  |  | 345 |      * @param string $mediatag HTML media tag
 | 
        
           |  |  | 346 |      * @param string $expected expected result
 | 
        
           |  |  | 347 |      */
 | 
        
           | 11 | efrain | 348 |     public function test_media_tags($mediatag, $expected): void {
 | 
        
           | 1 | efrain | 349 |         $actual = format_text($mediatag, FORMAT_MOODLE, ['filter' => false]);
 | 
        
           |  |  | 350 |         $this->assertEquals($expected, $actual);
 | 
        
           |  |  | 351 |     }
 | 
        
           |  |  | 352 |   | 
        
           |  |  | 353 |     /**
 | 
        
           |  |  | 354 |      * Test cases for the test_media_tags test.
 | 
        
           |  |  | 355 |      */
 | 
        
           |  |  | 356 |     public function media_tags_provider() {
 | 
        
           |  |  | 357 |         // Takes an array of attributes, then generates a test for each of them.
 | 
        
           |  |  | 358 |         $generatetestcases = function($prefix, array $attrs, array $templates) {
 | 
        
           |  |  | 359 |             return array_reduce($attrs, function($carry, $attr) use ($prefix, $templates) {
 | 
        
           |  |  | 360 |                 $testcase = [$prefix . '/' . $attr => [
 | 
        
           |  |  | 361 |                     sprintf($templates[0], $attr),
 | 
        
           |  |  | 362 |                     sprintf($templates[1], $attr)
 | 
        
           |  |  | 363 |                 ]];
 | 
        
           |  |  | 364 |                 return empty(array_values($carry)[0]) ? $testcase : $carry + $testcase;
 | 
        
           |  |  | 365 |             }, [[]]);
 | 
        
           |  |  | 366 |         };
 | 
        
           |  |  | 367 |   | 
        
           |  |  | 368 |         $audioattrs = [
 | 
        
           |  |  | 369 |             'preload="auto"', 'autoplay=""', 'loop=""', 'muted=""', 'controls=""',
 | 
        
           |  |  | 370 |             'crossorigin="anonymous"', 'crossorigin="use-credentials"'
 | 
        
           |  |  | 371 |         ];
 | 
        
           |  |  | 372 |         $videoattrs = [
 | 
        
           |  |  | 373 |             'crossorigin="anonymous"', 'crossorigin="use-credentials"',
 | 
        
           |  |  | 374 |             'poster="https://upload.wikimedia.org/wikipedia/en/1/14/Space_jam.jpg"',
 | 
        
           |  |  | 375 |             'preload="auto"', 'autoplay=""', 'playsinline=""', 'loop=""', 'muted=""',
 | 
        
           |  |  | 376 |             'controls=""', 'width="420"', 'height="69"'
 | 
        
           |  |  | 377 |         ];
 | 
        
           |  |  | 378 |         return $generatetestcases('Plain audio', $audioattrs + ['src="http://example.com/jam.wav"'], [
 | 
        
           |  |  | 379 |                 '<audio %1$s>Looks like you can\'t slam the jams.</audio>',
 | 
        
           |  |  | 380 |                 '<div class="text_to_html"><audio %1$s>Looks like you can\'t slam the jams.</audio></div>'
 | 
        
           |  |  | 381 |             ]) + $generatetestcases('Audio with one source', $audioattrs, [
 | 
        
           |  |  | 382 |                 '<audio %1$s><source src="http://example.com/getup.wav">No tasty jams for you.</audio>',
 | 
        
           |  |  | 383 |                 '<div class="text_to_html">' .
 | 
        
           |  |  | 384 |                     '<audio %1$s>' .
 | 
        
           |  |  | 385 |                         '<source src="http://example.com/getup.wav" />' .
 | 
        
           |  |  | 386 |                         'No tasty jams for you.' .
 | 
        
           |  |  | 387 |                     '</audio>' .
 | 
        
           |  |  | 388 |                 '</div>'
 | 
        
           |  |  | 389 |             ]) + $generatetestcases('Audio with multiple sources', $audioattrs, [
 | 
        
           |  |  | 390 |                 '<audio %1$s>' .
 | 
        
           |  |  | 391 |                     '<source src="http://example.com/getup.wav" type="audio/wav">' .
 | 
        
           |  |  | 392 |                     '<source src="http://example.com/getup.mp3" type="audio/mpeg">' .
 | 
        
           |  |  | 393 |                     '<source src="http://example.com/getup.ogg" type="audio/ogg">' .
 | 
        
           |  |  | 394 |                     'No tasty jams for you.' .
 | 
        
           |  |  | 395 |                 '</audio>',
 | 
        
           |  |  | 396 |                 '<div class="text_to_html">' .
 | 
        
           |  |  | 397 |                     '<audio %1$s>' .
 | 
        
           |  |  | 398 |                         '<source src="http://example.com/getup.wav" type="audio/wav" />' .
 | 
        
           |  |  | 399 |                         '<source src="http://example.com/getup.mp3" type="audio/mpeg" />' .
 | 
        
           |  |  | 400 |                         '<source src="http://example.com/getup.ogg" type="audio/ogg" />' .
 | 
        
           |  |  | 401 |                         'No tasty jams for you.' .
 | 
        
           |  |  | 402 |                     '</audio>' .
 | 
        
           |  |  | 403 |                 '</div>'
 | 
        
           |  |  | 404 |             ]) + $generatetestcases('Audio with sources and tracks', $audioattrs, [
 | 
        
           |  |  | 405 |                 '<audio %1$s>' .
 | 
        
           |  |  | 406 |                     '<source src="http://example.com/getup.wav" type="audio/wav">' .
 | 
        
           |  |  | 407 |                     '<track kind="subtitles" src="http://example.com/subtitles_en.vtt" label="English" srclang="en">' .
 | 
        
           |  |  | 408 |                     '<track kind="subtitles" src="http://example.com/subtitles_es.vtt" label="Espanol" srclang="es">' .
 | 
        
           |  |  | 409 |                     'No tasty jams for you.' .
 | 
        
           |  |  | 410 |                 '</audio>',
 | 
        
           |  |  | 411 |                 '<div class="text_to_html">' .
 | 
        
           |  |  | 412 |                     '<audio %1$s>' .
 | 
        
           |  |  | 413 |                         '<source src="http://example.com/getup.wav" type="audio/wav" />' .
 | 
        
           |  |  | 414 |                         '<track kind="subtitles" src="http://example.com/subtitles_en.vtt" label="English" srclang="en" />' .
 | 
        
           |  |  | 415 |                         '<track kind="subtitles" src="http://example.com/subtitles_es.vtt" label="Espanol" srclang="es" />' .
 | 
        
           |  |  | 416 |                         'No tasty jams for you.' .
 | 
        
           |  |  | 417 |                     '</audio>' .
 | 
        
           |  |  | 418 |                 '</div>'
 | 
        
           |  |  | 419 |             ]) + $generatetestcases('Plain video', $videoattrs + ['src="http://example.com/prettygood.mp4'], [
 | 
        
           |  |  | 420 |                 '<video %1$s>Oh, that\'s pretty bad 😦</video>',
 | 
        
           |  |  | 421 |                 '<div class="text_to_html"><video %1$s>Oh, that\'s pretty bad 😦</video></div>'
 | 
        
           |  |  | 422 |             ]) + $generatetestcases('Video with illegal subtag', $videoattrs + ['src="http://example.com/prettygood.mp4'], [
 | 
        
           |  |  | 423 |                 '<video %1$s><subtag></subtag>Oh, that\'s pretty bad 😦</video>',
 | 
        
           |  |  | 424 |                 '<div class="text_to_html"><video %1$s>Oh, that\'s pretty bad 😦</video></div>'
 | 
        
           |  |  | 425 |             ]) + $generatetestcases('Video with legal subtag', $videoattrs + ['src="http://example.com/prettygood.mp4'], [
 | 
        
           |  |  | 426 |                 '<video %1$s>Did not work <a href="http://example.com/prettygood.mp4">click here to download</a></video>',
 | 
        
           |  |  | 427 |                 '<div class="text_to_html"><video %1$s>Did not work <a href="http://example.com/prettygood.mp4">' .
 | 
        
           |  |  | 428 |                 'click here to download</a></video></div>'
 | 
        
           |  |  | 429 |             ]) + $generatetestcases('Video inside an inline tag', $videoattrs + ['src="http://example.com/prettygood.mp4'], [
 | 
        
           |  |  | 430 |                 '<em><video %1$s>Oh, that\'s pretty bad 😦</video></em>',
 | 
        
           |  |  | 431 |                 '<div class="text_to_html"><em><video %1$s>Oh, that\'s pretty bad 😦</video></em></div>'
 | 
        
           |  |  | 432 |             ]) + $generatetestcases('Video inside a block tag', $videoattrs + ['src="http://example.com/prettygood.mp4'], [
 | 
        
           |  |  | 433 |                 '<p><video %1$s>Oh, that\'s pretty bad 😦</video></p>',
 | 
        
           |  |  | 434 |                 '<div class="text_to_html"><p><video %1$s>Oh, that\'s pretty bad 😦</video></p></div>'
 | 
        
           |  |  | 435 |             ]) + $generatetestcases('Source tag without video or audio', $videoattrs, [
 | 
        
           |  |  | 436 |                 'some text <source src="http://example.com/getup.wav" type="audio/wav"> the end',
 | 
        
           |  |  | 437 |                 '<div class="text_to_html">some text  the end</div>'
 | 
        
           |  |  | 438 |             ]) + $generatetestcases('Video with one source', $videoattrs, [
 | 
        
           |  |  | 439 |                 '<video %1$s><source src="http://example.com/prettygood.mp4">Oh, that\'s pretty bad 😦</video>',
 | 
        
           |  |  | 440 |                 '<div class="text_to_html">' .
 | 
        
           |  |  | 441 |                     '<video %1$s>' .
 | 
        
           |  |  | 442 |                         '<source src="http://example.com/prettygood.mp4" />' .
 | 
        
           |  |  | 443 |                         'Oh, that\'s pretty bad 😦' .
 | 
        
           |  |  | 444 |                     '</video>' .
 | 
        
           |  |  | 445 |                 '</div>'
 | 
        
           |  |  | 446 |             ]) + $generatetestcases('Video with multiple sources', $videoattrs, [
 | 
        
           |  |  | 447 |                 '<video %1$s>' .
 | 
        
           |  |  | 448 |                     '<source src="http://example.com/prettygood.mp4" type="video/mp4">' .
 | 
        
           |  |  | 449 |                     '<source src="http://example.com/eljefe.mp4" type="video/mp4">' .
 | 
        
           |  |  | 450 |                     '<source src="http://example.com/turnitup.mov" type="video/mov">' .
 | 
        
           |  |  | 451 |                     'Oh, that\'s pretty bad 😦' .
 | 
        
           |  |  | 452 |                 '</video>',
 | 
        
           |  |  | 453 |                 '<div class="text_to_html">' .
 | 
        
           |  |  | 454 |                     '<video %1$s>' .
 | 
        
           |  |  | 455 |                         '<source src="http://example.com/prettygood.mp4" type="video/mp4" />' .
 | 
        
           |  |  | 456 |                         '<source src="http://example.com/eljefe.mp4" type="video/mp4" />' .
 | 
        
           |  |  | 457 |                         '<source src="http://example.com/turnitup.mov" type="video/mov" />' .
 | 
        
           |  |  | 458 |                         'Oh, that\'s pretty bad 😦' .
 | 
        
           |  |  | 459 |                     '</video>' .
 | 
        
           |  |  | 460 |                 '</div>'
 | 
        
           |  |  | 461 |             ]) + $generatetestcases('Video with sources and tracks', $audioattrs, [
 | 
        
           |  |  | 462 |                 '<video %1$s>' .
 | 
        
           |  |  | 463 |                     '<source src="http://example.com/getup.wav" type="audio/wav">' .
 | 
        
           |  |  | 464 |                     '<track kind="subtitles" src="http://example.com/subtitles_en.vtt" label="English" srclang="en">' .
 | 
        
           |  |  | 465 |                     '<track kind="subtitles" src="http://example.com/subtitles_es.vtt" label="Espanol" srclang="es">' .
 | 
        
           |  |  | 466 |                     'No tasty jams for you.' .
 | 
        
           |  |  | 467 |                 '</video>',
 | 
        
           |  |  | 468 |                 '<div class="text_to_html">' .
 | 
        
           |  |  | 469 |                     '<video %1$s>' .
 | 
        
           |  |  | 470 |                         '<source src="http://example.com/getup.wav" type="audio/wav" />' .
 | 
        
           |  |  | 471 |                         '<track kind="subtitles" src="http://example.com/subtitles_en.vtt" label="English" srclang="en" />' .
 | 
        
           |  |  | 472 |                         '<track kind="subtitles" src="http://example.com/subtitles_es.vtt" label="Espanol" srclang="es" />' .
 | 
        
           |  |  | 473 |                     'No tasty jams for you.' .
 | 
        
           |  |  | 474 |                     '</video>' .
 | 
        
           |  |  | 475 |                 '</div>'
 | 
        
           |  |  | 476 |             ]) + ['Video with invalid crossorigin' => [
 | 
        
           |  |  | 477 |                     '<video src="http://example.com/turnitup.mov" crossorigin="can i pls hab?">' .
 | 
        
           |  |  | 478 |                         'Oh, that\'s pretty bad 😦' .
 | 
        
           |  |  | 479 |                     '</video>',
 | 
        
           |  |  | 480 |                     '<div class="text_to_html">' .
 | 
        
           |  |  | 481 |                         '<video src="http://example.com/turnitup.mov">' .
 | 
        
           |  |  | 482 |                            'Oh, that\'s pretty bad 😦' .
 | 
        
           |  |  | 483 |                         '</video>' .
 | 
        
           |  |  | 484 |                     '</div>'
 | 
        
           |  |  | 485 |             ]] + ['Audio with invalid crossorigin' => [
 | 
        
           |  |  | 486 |                     '<audio src="http://example.com/getup.wav" crossorigin="give me. the jams.">' .
 | 
        
           |  |  | 487 |                         'nyemnyemnyem' .
 | 
        
           |  |  | 488 |                     '</audio>',
 | 
        
           |  |  | 489 |                     '<div class="text_to_html">' .
 | 
        
           |  |  | 490 |                         '<audio src="http://example.com/getup.wav">' .
 | 
        
           |  |  | 491 |                             'nyemnyemnyem' .
 | 
        
           |  |  | 492 |                         '</audio>' .
 | 
        
           |  |  | 493 |                     '</div>'
 | 
        
           |  |  | 494 |             ]] + ['Other attributes' => [
 | 
        
           |  |  | 495 |                 '<video src="http://example.com/turnitdown.mov" class="nofilter" data-something="data attribute" someattribute="somevalue" onclick="boom">' .
 | 
        
           |  |  | 496 |                     '<source src="http://example.com/getup.wav" type="audio/wav" class="shouldberemoved" data-sourcedata="source data" onmouseover="kill session" />' .
 | 
        
           |  |  | 497 |                     '<track src="http://example.com/subtitles_en.vtt" class="shouldberemoved" data-trackdata="track data" onmouseover="removeme" />' .
 | 
        
           |  |  | 498 |                     'Do not remove attribute class but remove other attributes' .
 | 
        
           |  |  | 499 |                 '</video>',
 | 
        
           |  |  | 500 |                 '<div class="text_to_html">' .
 | 
        
           |  |  | 501 |                     '<video src="http://example.com/turnitdown.mov" class="nofilter">' .
 | 
        
           |  |  | 502 |                         '<source src="http://example.com/getup.wav" type="audio/wav" />' .
 | 
        
           |  |  | 503 |                         '<track src="http://example.com/subtitles_en.vtt" />' .
 | 
        
           |  |  | 504 |                         'Do not remove attribute class but remove other attributes' .
 | 
        
           |  |  | 505 |                     '</video>' .
 | 
        
           |  |  | 506 |                 '</div>'
 | 
        
           |  |  | 507 |             ]];
 | 
        
           |  |  | 508 |     }
 | 
        
           |  |  | 509 | }
 |