Files
AggloSign/backend/app/Signature/TemplateSanitizer.php
T
2026-09-30 16:27:32 +02:00

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