53 lines
2.1 KiB
PHP
53 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Signature;
|
|
|
|
/**
|
|
* Nettoie le HTML d'un template avant enregistrement (sans dépendance au framework).
|
|
*
|
|
* Volontairement basé sur des expressions régulières et non sur DOMDocument :
|
|
* un parseur HTML déplacerait les marqueurs {{#if}} placés entre deux <tr>,
|
|
* ce qui casserait les templates.
|
|
*
|
|
* Défense en profondeur : les aperçus sont en plus affichés dans des iframes « sandbox ».
|
|
*/
|
|
final class TemplateSanitizer
|
|
{
|
|
/** Balises retirées avec leur contenu. */
|
|
private const BLOCK_TAGS = ['script', 'iframe', 'object', 'embed', 'applet', 'form', 'noscript', 'template', 'svg', 'math'];
|
|
|
|
/** Balises orphelines retirées. */
|
|
private const VOID_TAGS = ['base', 'meta', 'link', 'input', 'button', 'select', 'textarea', 'frame', 'frameset'];
|
|
|
|
public function clean(string $html): string
|
|
{
|
|
foreach (self::BLOCK_TAGS as $tag) {
|
|
$html = preg_replace('#<'.$tag.'\b[^>]*>.*?</'.$tag.'\s*>#is', '', $html);
|
|
$html = preg_replace('#<'.$tag.'\b[^>]*/?>#i', '', $html); // balise ouvrante sans fermeture
|
|
}
|
|
foreach (self::VOID_TAGS as $tag) {
|
|
$html = preg_replace('#</?'.$tag.'\b[^>]*>#i', '', $html);
|
|
}
|
|
|
|
// Gestionnaires d'événements : onclick=, onerror=…
|
|
$html = preg_replace('#\s+on[a-z]+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)#i', '', $html);
|
|
|
|
// URL dangereuses dans href / src / action / style url()
|
|
$html = preg_replace_callback(
|
|
'#\s(href|src|action|formaction|xlink:href|background)\s*=\s*("([^"]*)"|\'([^\']*)\'|([^\s>]+))#i',
|
|
function (array $m) {
|
|
$value = html_entity_decode($m[3] !== '' ? $m[3] : ($m[4] ?? $m[5] ?? ''), ENT_QUOTES | ENT_HTML5);
|
|
$normalized = strtolower(preg_replace('/[\s\x00-\x1f]+/', '', $value));
|
|
|
|
return preg_match('#^(javascript|vbscript|data):#', $normalized) && ! str_starts_with($normalized, 'data:image/')
|
|
? ''
|
|
: $m[0];
|
|
},
|
|
$html,
|
|
);
|
|
$html = preg_replace('#expression\s*\(|javascript\s*:#i', '', $html);
|
|
|
|
return trim($html);
|
|
}
|
|
}
|