Dag allemaal,

ik ben laatste uur even bezig geweest om geposte html te parsen naar iets wat veiliger is voor .. mezelf.
Ik heb geprobeer om eigenlijk als eerste dom document->validate() te doen, maar dat duurde zoo lang, en dan falen op 'external' gedoe waar ik ook al niet uitkwam, als iemand weet hoe ik da kan fixen ook graag :D

Ik heb een set aan html elementen, en classnames die ik wel wil toelaten.

Wat vinden jullie hiervan?


$allowedTags = '<tr><td><i><span>';
$allowedClasses = array('fa','fa-exclamation','fa-questionmark','tooltip');


$foundClasses = false;
$cleanHtml = strip_tags($html,$allowedTags);
preg_match_all('/class[ \t]*=[ \t]*"[^"]+"/',$cleanHtml,$foundClasses);
if(is_array($foundClasses)) {
    foreach($foundClasses[0] as $cClass) {
        $finalClass = $cClass;
        $tClass = str_replace('class=','',str_replace('"','',$cClass));
        $eClass = explode(' ',$tClass);

        $returnClass = '';
        foreach($eClass as $sClass) {
            if(in_array($sClass, $allowedClasses)) {
                if($returnClass !== '') { $returnClass .= ' '; }
                $returnClass .= $sClass;
            }
        }
        if(strlen($returnClass) > 0) {
            $returnClass = 'class="' . $returnClass . '"';
        }
        $cleanHtml = str_replace($cClass,$returnClass,$cleanHtml);
    }
}

p.s. heb er nog geen functie van gemaakt of geheel ingekort.
Dat wil ik pas doen als ik helemaal klaar ben en het werkt naar behoren en is geoptimaliseerd.
Ik zou je regex aanpassen naar:
preg_match_all('/class[ \t]*=[ \t]*"([^"]+)"/',$cleanHtml,$foundClasses);
In je eerste match (doe maar een print_r($foundClasses); ) zitten dan alleen de classes. Met preg_split() kun je die dan "exploderen" op whitespace (dat is "\s", volgens mij kan dat net zo goed als steeds "[ \t]"). Met array_intersect() houd je dit lijstje dan tegen je $allowedClasses. Vervolgens implode() je de het resultaat weer voor je $cleanHtml (zelf nog even "class=" + quotes d'r bij plakken).
Hi rob,

Ik ben nog niet zo heel goed met regex.
Door je [^"] krijg ik nu in mn regex result
[0] t gehele resultaat zoals ik al had, [1] alles wat binnen de "" valt.
Cool!
Die kan ik valideren en de regel van [0] vervangen (met class="" uiteraard)
Snap alleen niet goed wat je bedoel met intersect enz.
Zal daar komende tijd eens meer in verdiepen :)

Graag zou ik ook de dom->validate() werkend willen hebben.
Door een wizard dat heel behoorlijk dynamisch is, maak ik tabel regels. Die ik hiermee dus valideer en bijwerk waar nodig.
Als ik van tevoren al weet dat de markup al niet goed is, kan ik daar dan al niet meer in de fout gaan.

Thanks!
Ok, nog even in slow-motion. Ik had vanmorgen een beetje haast ;-)

Die [^"] had je zelf al in je regex staan. Dat betekent gewoon 'alles behalve "'. Dus je zoekt vanaf een " 'alles behalve een "' (en dan weer een "). Doordat je d'r (...) omheen zet ga je dat stukje "groeperen", en krijg je 'm apart terug. Dan hoef je niet zelf te knutselen (jouw regel 11).

Overigens kan een attribuut-waarde ook met single-quotes omsloten worden. Die glippen d'r nu gewoon tussendoor. En als het slechts een enkele class is (geen spaties) kan het ook zonder quotes. Merk op dat dit een behoorlijk "gevecht" kan zijn met je "gebruikers": https://en.wikipedia.org/wiki/Samy_%28computer_worm%29

Met een intersect krijg je alleen de waarden die in beide arrays voorkomen. Als je dus een lijstje met class names hebt, en je intersect die met de $allowedClasses, dan hou je van het originele lijstje alleen diegene over die "allowed" zijn.

Samenvattend:

//ik pak de voorloop-spaties voor de "class" ook mee, zodat die ook verdwijnen als er geen class names overblijven
//tevens PREG_SET_ORDER, dan houd je alles in een setje compleet
//en ik check dus voor single-quotes, double-quotes, of (| teken) geen quotes
if(preg_match_all('/\\s+class\\s*=\\s*(\'([^\']*)\'|"([^"]*)"|(\\w+))/',$cleanHtml,$foundClasses,PREG_SET_ORDER))
  foreach($foundClasses as $cClass){
    $full = $cClass[0]; //full match (dus incl. class="...")
    $eClass = preg_split('/\s+/',array_pop($cClass)); //array met class names
    $returnClass = implode(' ',array_intersect($eClass,$allowedClasses)); //alleen de toegestane class names
    if(strlen($returnClass) > 0) {
        $returnClass = ' class="' . $returnClass . '"'; //let op: hier weer een extra spatie voor "class"
    }
    $cleanHtml = str_replace($full,$returnClass,$cleanHtml);
  }
Awesome Rob!
Ik snap het helemaal het is gelukt :D bedankt voor de jip janeke :-)
Ik ga ook alles wat niet geen class attribute is weggooien uit de html :)

Heb die link idd vaker gezien
Tijd voor vakantie!
Hi Thomas,

je hebt helemaal gelijk. We zullen ook eerst alle ascii hex en urlsafe characters moeten converteren naar normale karakters en vervolgrns deze 'filter' eroverheen te halen.
Na de vakantie ga ik me hier eens in verdiepen. Ik wil namelijk niet simpelweg %, ; etc willen strippen, omdat dit zeker wel acceptabele karakters zijn als input van de gebruikers.

Reageren