Files
slim-blog/tests/Shared/HtmlSanitizerTest.php
2026-03-16 01:47:07 +01:00

240 lines
7.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Shared;
use App\Shared\Html\HtmlPurifierFactory;
use App\Shared\Html\HtmlSanitizer;
use PHPUnit\Framework\TestCase;
/**
* Tests unitaires pour HtmlSanitizer.
*
* Vérifie que HTMLPurifier supprime bien les contenus dangereux
* (XSS, balises non autorisées, schémas URI interdits) et conserve
* les balises légitimes produites par l'éditeur Trumbowyg.
*
* Ces tests utilisent une vraie instance HTMLPurifier (pas de mock)
* car c'est le comportement de purification lui-même qui est testé.
*/
final class HtmlSanitizerTest extends TestCase
{
private HtmlSanitizer $sanitizer;
/**
* Crée une instance réelle de HtmlSanitizer avant chaque test.
*/
protected function setUp(): void
{
$purifier = HtmlPurifierFactory::create(sys_get_temp_dir() . '/htmlpurifier_tests');
$this->sanitizer = new HtmlSanitizer($purifier);
}
// ── Balises autorisées ─────────────────────────────────────────
/**
* Les balises de texte courantes doivent être conservées.
*/
public function testTextTagsPreserved(): void
{
$html = '<p>Un <strong>texte</strong> avec <em>emphase</em> et <u>soulignement</u>.</p>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('<strong>texte</strong>', $result);
$this->assertStringContainsString('<em>emphase</em>', $result);
$this->assertStringContainsString('<u>soulignement</u>', $result);
}
/**
* Les titres h1 à h6 doivent être conservés.
*/
public function testHeadingsPreserved(): void
{
$html = '<h1>Titre 1</h1><h2>Titre 2</h2><h3>Titre 3</h3>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('<h1>', $result);
$this->assertStringContainsString('<h2>', $result);
$this->assertStringContainsString('<h3>', $result);
}
/**
* Les listes ordonnées et non ordonnées doivent être conservées.
*/
public function testListsPreserved(): void
{
$html = '<ul><li>Item 1</li><li>Item 2</li></ul><ol><li>A</li></ol>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('<ul>', $result);
$this->assertStringContainsString('<ol>', $result);
$this->assertStringContainsString('<li>', $result);
}
/**
* Les liens avec href http/https doivent être conservés.
*/
public function testHttpLinksPreserved(): void
{
$html = '<a href="https://example.com">Lien</a>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('href="https://example.com"', $result);
$this->assertStringContainsString('Lien', $result);
}
/**
* Les images avec src doivent être conservées.
*/
public function testImagesPreserved(): void
{
$html = '<img src="https://example.com/image.jpg" alt="Description">';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('<img', $result);
$this->assertStringContainsString('src="https://example.com/image.jpg"', $result);
}
/**
* Les blocs de code doivent être conservés.
*/
public function testPreTagPreserved(): void
{
$html = '<pre>code ici</pre>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('<pre>', $result);
}
// ── Balises et attributs dangereux — suppression XSS ───────────
/**
* Les balises <script> doivent être supprimées.
*/
public function testScriptTagRemoved(): void
{
$html = '<p>Texte</p><script>alert("xss")</script>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('<script>', $result);
$this->assertStringNotContainsString('alert(', $result);
}
/**
* Les attributs onclick et autres handlers JavaScript doivent être supprimés.
*/
public function testJavascriptAttributesRemoved(): void
{
$html = '<p onclick="alert(1)" onmouseover="evil()">Texte</p>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('onclick', $result);
$this->assertStringNotContainsString('onmouseover', $result);
}
/**
* Les liens javascript: doivent être supprimés.
*/
public function testJavascriptLinkRemoved(): void
{
$html = '<a href="javascript:alert(1)">Cliquez</a>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('javascript:', $result);
}
/**
* Les liens data: doivent être supprimés.
*/
public function testDataLinkRemoved(): void
{
$html = '<a href="data:text/html,<script>alert(1)</script>">XSS</a>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('data:', $result);
}
/**
* La balise <iframe> doit être supprimée.
*/
public function testIframeTagRemoved(): void
{
$html = '<iframe src="https://evil.com"></iframe>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('<iframe', $result);
}
/**
* La balise <object> doit être supprimée.
*/
public function testObjectTagRemoved(): void
{
$html = '<object data="malware.swf"></object>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('<object', $result);
}
/**
* La balise <form> doit être supprimée.
*/
public function testFormTagRemoved(): void
{
$html = '<form action="/steal"><input type="password"></form>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('<form', $result);
$this->assertStringNotContainsString('<input', $result);
}
// ── Cas limites ────────────────────────────────────────────────
/**
* Une chaîne vide doit retourner une chaîne vide (ou quasi-vide).
*/
public function testEmptyStringReturnsEmptyOrBlank(): void
{
$result = $this->sanitizer->sanitize('');
$this->assertSame('', trim($result));
}
/**
* Du texte brut sans balises doit être conservé.
*/
public function testPlainTextWithoutTags(): void
{
$html = 'Bonjour le monde';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('Bonjour le monde', $result);
}
/**
* Les attributs CSS text-align doivent être conservés.
*/
public function testStyleTextAlignAttributePreserved(): void
{
$html = '<p style="text-align: center;">Centré</p>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringContainsString('text-align', $result);
}
/**
* Les propriétés CSS autres que text-align doivent être supprimées.
*/
public function testOtherCssPropertiesRemoved(): void
{
$html = '<p style="color: red; background: url(evil.php);">Texte</p>';
$result = $this->sanitizer->sanitize($html);
$this->assertStringNotContainsString('color', $result);
$this->assertStringNotContainsString('background', $result);
}
}