Очистка HTML: удаление элемента условно на основе его атрибутов

Согласно smoketest очистителя HTML , «некорректные» URI иногда отбрасываются, чтобы оставить тег привязки без атрибута, например

<a href="javascript:document.location='http://www.google.com/'">XSS</a> становится <a>XSS</a>

… а также время от времени сокращается до протокола, например

<a href="http://1113982867/">XSS</a> становится <a href="http:/">XSS</a>

Хотя это беспроблемно, само по себе, это немного уродливо. Вместо того, чтобы пытаться вытеснить их с помощью регулярных выражений, я надеялся использовать собственные возможности библиотеки HTML Purifier / инжекторы / плагины / whathaveyou.

Точка отсчета: обработка атрибутов

Условно удалить атрибут в HTMLPurifier легко. Здесь библиотека предлагает класс HTMLPurifier_AttrTransform с методом confiscateAttr() .

Хотя я лично не использую функции confiscateAttr() , я использую HTMLPurifier_AttrTransform в соответствии с этим потоком, чтобы добавить target="_blank" ко всем якорям.

 // more configuration stuff up here $htmlDef = $htmlPurifierConfiguration->getHTMLDefinition(true); $anchor = $htmlDef->addBlankElement('a'); $anchor->attr_transform_post[] = new HTMLPurifier_AttrTransform_Target(); // purify down here 

HTMLPurifier_AttrTransform_Target – очень простой класс.

 class HTMLPurifier_AttrTransform_Target extends HTMLPurifier_AttrTransform { public function transform($attr, $config, $context) { // I could call $this->confiscateAttr() here to throw away an // undesired attribute $attr['target'] = '_blank'; return $attr; } } 

Естественно, эта часть работает как шарм.

Обрабатывающие элементы

Возможно, я не слишком HTMLPurifier_TagTransform щурясь в HTMLPurifier_TagTransform или смотрю не в том месте (местах), или вообще не понимаю, но я не могу найти способ условно удалить элементы .

Скажем, что-то вроде:

 // more configuration stuff up here $htmlDef = $htmlPurifierConfiguration->getHTMLDefinition(true); $anchor = $htmlDef->addElementHandler('a'); $anchor->elem_transform_post[] = new HTMLPurifier_ElementTransform_Cull(); // add target as per 'point of reference' here // purify down here 

С классом Cull, расширяющим то, что имеет свойство confiscateElement() , или сопоставимым, в котором я мог проверить отсутствующий атрибут href или атрибут href с контентом http:/ .

HTMLPurifier_Filter

Я понимаю, что могу создать фильтр, но примеры (Youtube.php и ExtractStyleBlocks.php) предполагают, что я буду использовать в нем регулярные выражения, которых я бы действительно избегал, если это вообще возможно . Я надеюсь на встроенное или квази-бортовое решение, которое использует превосходные возможности синтаксического анализа HTML Purifier.

Возвращение null в дочернем классе HTMLPurifier_AttrTransform к сожалению, не сокращает его.

У кого-нибудь есть умные идеи, или я придерживаюсь регулярными выражениями? 🙂

Успех! Благодаря Ambush Commander и mcgrailm в другом вопросе я теперь использую довольно простое решение:

 // a bit of context $htmlDef = $this->configuration->getHTMLDefinition(true); $anchor = $htmlDef->addBlankElement('a'); // HTMLPurifier_AttrTransform_RemoveLoneHttp strips 'href="http:/"' from // all anchor tags (see first post for class detail) $anchor->attr_transform_post[] = new HTMLPurifier_AttrTransform_RemoveLoneHttp(); // this is the magic! We're making 'href' a required attribute (note the // asterisk) - now HTML Purifier removes <a></a>, as well as // <a href="http:/"></a> after HTMLPurifier_AttrTransform_RemoveLoneHttp // is through with it! $htmlDef->addAttribute('a', 'href*', new HTMLPurifier_AttrDef_URI()); 

Он работает, он работает , bahahahaHAHAHAHAnhͥͤͫğͮ͑̆ͦó̓̉ͬ͋hͧ̆̈̉ğ̈͐̈a̾̈̑ͨô̔̄̑̇ḡh̘̝͊̐ͩͥ̋ͤ͛g̦̣̙̙̒ͥ̐̔o̤̣hg͓̈͋̇̓̆ä͖̩̯̥͕̐ͮ̒o̶ͬ̽̍ͮ̾ͮ͢҉̩͉̘͓̙̦̩̹͍̹̠̕g̵̡͔̙͉̠̙̩͚͑ͥ̓͛̋͗̍̽͋͑̈̚ … ! * маниакальный смех, шумящие шумы, киль с улыбкой на лице *

Тот факт, что вы не можете удалить элементы с помощью TagTransform, похоже, был деталью реализации. Классический механизм удаления узлов (более высокий уровень, чем только теги) заключается в использовании Инжектора.

В любом случае, определенная часть функциональности, которую вы ищете, уже реализована как% AutoFormat.RemoveEmpty

Для прочтения это мое текущее решение. Он работает, но полностью обходит HTML-очиститель.

 /** * Removes <a></a> and <a href="http:/"></a> tags from the purified * HTML. * @todo solve this with an injector? * @param string $purified The purified HTML * @return string The purified HTML, sans pointless anchors. */ private function anchorCull($purified) { if (empty($purified)) return ''; // re-parse HTML $domTree = new DOMDocument(); $domTree->loadHTML($purified); // find all anchors (even good ones) $anchors = $domTree->getElementsByTagName('a'); // collect bad anchors (destroying them in this loop breaks the DOM) $destroyNodes = array(); for ($i = 0; ($i < $anchors->length); $i++) { $anchor = $anchors->item($i); $href = $anchor->attributes->getNamedItem('href'); // <a></a> if (is_null($href)) { $destroyNodes[] = $anchor; // <a href="http:/"></a> } else if ($href->nodeValue == 'http:/') { $destroyNodes[] = $anchor; } } // destroy the collected nodes foreach ($destroyNodes as $node) { // preserve content $retain = $node->childNodes; for ($i = 0; ($i < $retain->length); $i++) { $rnode = $retain->item($i); $node->parentNode->insertBefore($rnode, $node); } // actually destroy the node $node->parentNode->removeChild($node); } // strip out HTML out of DOM structure string $html = $domTree->saveHTML(); $begin = strpos($html, '<body>') + strlen('<body>'); $end = strpos($html, '</body>'); return substr($html, $begin, $end - $begin); } 

У меня все еще было бы довольно хорошее решение для очистки HTML для этого, так что, как хэдз-ап, этот ответ не станет самопринятым. Но в случае, если лучший ответ не закончится, по крайней мере, это может помочь тем, у кого есть подобные проблемы. 🙂