Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

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