48 lines
1.5 KiB
PHP
48 lines
1.5 KiB
PHP
|
|
<?php
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
class TaskCertHtml
|
||
|
|
{
|
||
|
|
private const ALLOWED_TAGS = '<p><br><strong><em><u><h2><h3><ol><ul><li><a>';
|
||
|
|
|
||
|
|
public static function sanitize(string $html): string
|
||
|
|
{
|
||
|
|
$html = trim($html);
|
||
|
|
if ($html === '') {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
// Strip all tags except the whitelist
|
||
|
|
$html = strip_tags($html, self::ALLOWED_TAGS);
|
||
|
|
|
||
|
|
// Sanitize <a> tags: keep only href, add target="_blank" and rel="noopener"
|
||
|
|
$html = preg_replace_callback(
|
||
|
|
'/<a\s[^>]*>/i',
|
||
|
|
static function (array $match): string {
|
||
|
|
$tag = $match[0];
|
||
|
|
// Extract href value
|
||
|
|
if (preg_match('/href\s*=\s*"([^"]*)"/i', $tag, $hrefMatch)) {
|
||
|
|
$href = $hrefMatch[1];
|
||
|
|
// Block javascript: URLs
|
||
|
|
if (preg_match('/^\s*javascript\s*:/i', $href)) {
|
||
|
|
return '<a>';
|
||
|
|
}
|
||
|
|
return '<a href="' . htmlspecialchars($href, ENT_QUOTES, 'UTF-8') . '" target="_blank" rel="noopener">';
|
||
|
|
}
|
||
|
|
return '<a>';
|
||
|
|
},
|
||
|
|
$html
|
||
|
|
) ?? $html;
|
||
|
|
|
||
|
|
// Remove empty paragraphs that Quill sometimes produces
|
||
|
|
$html = preg_replace('/<p>\s*<br\s*\/?>\s*<\/p>/', '', $html) ?? $html;
|
||
|
|
|
||
|
|
return trim($html);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function isHtml(string $text): bool
|
||
|
|
{
|
||
|
|
return $text !== strip_tags($text);
|
||
|
|
}
|
||
|
|
}
|