RemoveForeignElements.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <?php
  2. /**
  3. * Removes all unrecognized tags from the list of tokens.
  4. *
  5. * This strategy iterates through all the tokens and removes unrecognized
  6. * tokens. If a token is not recognized but a TagTransform is defined for
  7. * that element, the element will be transformed accordingly.
  8. */
  9. class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy
  10. {
  11. public function execute($tokens, $config, $context) {
  12. $definition = $config->getHTMLDefinition();
  13. $generator = new HTMLPurifier_Generator($config, $context);
  14. $result = array();
  15. $escape_invalid_tags = $config->get('Core.EscapeInvalidTags');
  16. $remove_invalid_img = $config->get('Core.RemoveInvalidImg');
  17. // currently only used to determine if comments should be kept
  18. $trusted = $config->get('HTML.Trusted');
  19. $comment_lookup = $config->get('HTML.AllowedComments');
  20. $comment_regexp = $config->get('HTML.AllowedCommentsRegexp');
  21. $check_comments = $comment_lookup !== array() || $comment_regexp !== null;
  22. $remove_script_contents = $config->get('Core.RemoveScriptContents');
  23. $hidden_elements = $config->get('Core.HiddenElements');
  24. // remove script contents compatibility
  25. if ($remove_script_contents === true) {
  26. $hidden_elements['script'] = true;
  27. } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) {
  28. unset($hidden_elements['script']);
  29. }
  30. $attr_validator = new HTMLPurifier_AttrValidator();
  31. // removes tokens until it reaches a closing tag with its value
  32. $remove_until = false;
  33. // converts comments into text tokens when this is equal to a tag name
  34. $textify_comments = false;
  35. $token = false;
  36. $context->register('CurrentToken', $token);
  37. $e = false;
  38. if ($config->get('Core.CollectErrors')) {
  39. $e =& $context->get('ErrorCollector');
  40. }
  41. foreach($tokens as $token) {
  42. if ($remove_until) {
  43. if (empty($token->is_tag) || $token->name !== $remove_until) {
  44. continue;
  45. }
  46. }
  47. if (!empty( $token->is_tag )) {
  48. // DEFINITION CALL
  49. // before any processing, try to transform the element
  50. if (
  51. isset($definition->info_tag_transform[$token->name])
  52. ) {
  53. $original_name = $token->name;
  54. // there is a transformation for this tag
  55. // DEFINITION CALL
  56. $token = $definition->
  57. info_tag_transform[$token->name]->
  58. transform($token, $config, $context);
  59. if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name);
  60. }
  61. if (isset($definition->info[$token->name])) {
  62. // mostly everything's good, but
  63. // we need to make sure required attributes are in order
  64. if (
  65. ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) &&
  66. $definition->info[$token->name]->required_attr &&
  67. ($token->name != 'img' || $remove_invalid_img) // ensure config option still works
  68. ) {
  69. $attr_validator->validateToken($token, $config, $context);
  70. $ok = true;
  71. foreach ($definition->info[$token->name]->required_attr as $name) {
  72. if (!isset($token->attr[$name])) {
  73. $ok = false;
  74. break;
  75. }
  76. }
  77. if (!$ok) {
  78. if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name);
  79. continue;
  80. }
  81. $token->armor['ValidateAttributes'] = true;
  82. }
  83. if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) {
  84. $textify_comments = $token->name;
  85. } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) {
  86. $textify_comments = false;
  87. }
  88. } elseif ($escape_invalid_tags) {
  89. // invalid tag, generate HTML representation and insert in
  90. if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text');
  91. $token = new HTMLPurifier_Token_Text(
  92. $generator->generateFromToken($token)
  93. );
  94. } else {
  95. // check if we need to destroy all of the tag's children
  96. // CAN BE GENERICIZED
  97. if (isset($hidden_elements[$token->name])) {
  98. if ($token instanceof HTMLPurifier_Token_Start) {
  99. $remove_until = $token->name;
  100. } elseif ($token instanceof HTMLPurifier_Token_Empty) {
  101. // do nothing: we're still looking
  102. } else {
  103. $remove_until = false;
  104. }
  105. if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed');
  106. } else {
  107. if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed');
  108. }
  109. continue;
  110. }
  111. } elseif ($token instanceof HTMLPurifier_Token_Comment) {
  112. // textify comments in script tags when they are allowed
  113. if ($textify_comments !== false) {
  114. $data = $token->data;
  115. $token = new HTMLPurifier_Token_Text($data);
  116. } elseif ($trusted || $check_comments) {
  117. // always cleanup comments
  118. $trailing_hyphen = false;
  119. if ($e) {
  120. // perform check whether or not there's a trailing hyphen
  121. if (substr($token->data, -1) == '-') {
  122. $trailing_hyphen = true;
  123. }
  124. }
  125. $token->data = rtrim($token->data, '-');
  126. $found_double_hyphen = false;
  127. while (strpos($token->data, '--') !== false) {
  128. $found_double_hyphen = true;
  129. $token->data = str_replace('--', '-', $token->data);
  130. }
  131. if ($trusted || !empty($comment_lookup[trim($token->data)]) || ($comment_regexp !== NULL && preg_match($comment_regexp, trim($token->data)))) {
  132. // OK good
  133. if ($e) {
  134. if ($trailing_hyphen) {
  135. $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed');
  136. }
  137. if ($found_double_hyphen) {
  138. $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed');
  139. }
  140. }
  141. } else {
  142. if ($e) {
  143. $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
  144. }
  145. continue;
  146. }
  147. } else {
  148. // strip comments
  149. if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed');
  150. continue;
  151. }
  152. } elseif ($token instanceof HTMLPurifier_Token_Text) {
  153. } else {
  154. continue;
  155. }
  156. $result[] = $token;
  157. }
  158. if ($remove_until && $e) {
  159. // we removed tokens until the end, throw error
  160. $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until);
  161. }
  162. $context->destroy('CurrentToken');
  163. return $result;
  164. }
  165. }
  166. // vim: et sw=4 sts=4