Purificador HTML: removendo um elemento condicionalmente com base em seus atributos

De acordo com o HTML Purifier smoketest , URIs “malformados” são ocasionalmente descartados para deixar uma marca de âncora sem atributo, por exemplo,

XSS se torna XSS

… assim como ocasionalmente sendo despojado para o protocolo, por exemplo,

XSS se torna XSS

Embora isso não seja problemático, per se, é um pouco feio. Em vez de tentar tirar isso com expressões regulares, eu estava esperando usar os resources / injetores / plug-ins / whathaveyou da HTML Purifier.

Ponto de referência: Atributos de manipulação

A remoção condicional de um atributo no HTMLPurifier é fácil. Aqui, a biblioteca oferece a class HTMLPurifier_AttrTransform com o método confiscateAttr() .

Embora eu não use pessoalmente a funcionalidade de confiscateAttr() , eu uso um HTMLPurifier_AttrTransform conforme esse tópico para adicionar target="_blank" a todas as âncoras.

 // 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 é uma class muito simples, é claro.

 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; } } 

Essa parte funciona como um encanto, naturalmente.

Manipulação de elementos

Talvez eu não esteja apertando os olhos o suficiente em HTMLPurifier_TagTransform , ou estou olhando para o (s) lugar (s) errado, ou geralmente não entendi, mas não consigo descobrir uma maneira de remover condicionalmente os elementos .

Diga, algo com o objective de:

 // 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 

Com a class Cull estendendo algo que tenha uma habilidade confiscateElement() , ou comparável, onde eu poderia verificar por um atributo href faltando ou um atributo href com o conteúdo http:/ .

HTMLPurifier_Filter

Eu entendo que eu poderia criar um filtro, mas os exemplos (Youtube.php e ExtractStyleBlocks.php) sugerem que eu estaria usando expressões regulares nisso, o que eu realmente preferiria evitar, se for possível . Estou esperando uma solução a bordo ou quase integrada que faça uso das excelentes capacidades de análise do HTML Purifier.

Retornar null em uma class-filho de HTMLPurifier_AttrTransform infelizmente, não o corta.

Alguém tem idéias inteligentes, ou estou preso com regexes? 🙂

Sucesso! Graças ao Comandante da Emboscada e ao mcgrailm em outra questão , agora uso uma solução hilariantemente simples:

 // 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 , as well as //  after HTMLPurifier_AttrTransform_RemoveLoneHttp // is through with it! $htmlDef->addAttribute('a', 'href*', new HTMLPurifier_AttrDef_URI()); 

Funciona, funciona , bahahahaHAHAHAhanhͥͤͫğͮ͑̆ͦó̓̉ͬ͋hͧ̆̈̉ğ̈͐̈a̾̈̑ͨô̔̄̑̇ḡh̘̝͊̐ͩͥ̋ͤ͛g̦̣̙̙̒ͥ̐̔o̤̣hg͓̈͋̇̓̆ä͖̩̯̥͕̐ͮ̒o̶ͬ̽̍ͮ̾ͮ͢҉̩͉̘͓̙̦̩̹͍̹̠̕g̵̡͔̙͉̠̙̩͚͑ͥ̓͛̋͗̍̽͋͑̈̚ … ! * risada maníaca, ruídos de gargalhadas, quilhas com um sorriso no rosto *

O fato de não poder remover elementos com um TagTransform parece ter sido um detalhe de implementação. O mecanismo clássico para remover nós (um nível mais alto do que o simples) é usar um Injetor.

De qualquer forma, a peça particular de funcionalidade que você está procurando já foi implementada como% AutoFormat.RemoveEmpty

Para a leitura, esta é a minha solução atual. Funciona, mas ignora completamente o Purificador de HTML.

 /** * Removes  and  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'); //  if (is_null($href)) { $destroyNodes[] = $anchor; //  } 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, '') + strlen(''); $end = strpos($html, ''); return substr($html, $begin, $end - $begin); } 

Eu ainda preferiria ter uma boa solução de Purificador HTML para isso, então, assim como um heads-up, essa resposta não acabará por ser aceita. Mas no caso de uma resposta melhor acabar acontecendo, pelo menos isso pode ajudar aqueles com problemas semelhantes. 🙂