Files
annu-kute-ced/tests/php/video-card-test.php
T

124 lines
5.2 KiB
PHP

<?php
/**
* Tests unitaires pour le helper e() (includes/security.php)
* et le partial renderVideoCard() (includes/partials/video-card.php)
*
* Non-régression XSS (SEC-1) : les données de l'API PeerTube (titres,
* chaînes, vignettes, avatars) ne doivent jamais ressortir telles quelles
* dans le HTML d'une carte vidéo.
*/
// --- e() ---------------------------------------------------------------------
assertEquals(
'&lt;script&gt;alert(1)&lt;/script&gt;',
e('<script>alert(1)</script>'),
'e() échappe les balises HTML'
);
assertEquals(
'&quot;guillemets&quot; &#039;apostrophes&#039;',
e('"guillemets" \'apostrophes\''),
'e() échappe guillemets et apostrophes (ENT_QUOTES)'
);
assertEquals('&amp;', e('&'), 'e() échappe l\'esperluette');
assertEquals('', e(null), 'e() convertit null en chaîne vide');
assertEquals('42', e(42), 'e() convertit les nombres en chaîne');
// --- renderVideoCard : structure de base -------------------------------------
$video = [
'id' => 'abc-123',
'title' => 'Ma vidéo',
'thumbnail' => 'https://videos.example/lazy/abc.jpg',
'duration' => 125,
'channel' => 'Ma chaîne',
'channelAvatar' => 'https://videos.example/avatars/a.png',
'views' => 42,
'date' => date('Y-m-d H:i:s'),
];
$html = renderVideoCard($video);
assertContains('class="video-card"', $html, 'renderVideoCard génère une carte vidéo');
assertContains('data-video-id="abc-123"', $html, 'renderVideoCard expose l\'identifiant vidéo');
assertContains('src="https://videos.example/lazy/abc.jpg"', $html, 'renderVideoCard affiche la vignette');
assertContains('<h3 class="video-title">Ma vidéo</h3>', $html, 'renderVideoCard affiche le titre');
assertContains('<span class="channel-name">Ma chaîne</span>', $html, 'renderVideoCard affiche la chaîne');
assertContains('class="channel-avatar"', $html, 'renderVideoCard affiche l\'avatar personnalisé');
assertContains('2:05', $html, 'renderVideoCard affiche la durée formatée');
// --- renderVideoCard : avatar par défaut --------------------------------------
$videoDefaultAvatar = $video;
$videoDefaultAvatar['channelAvatar'] = 'img/default-avatar.png';
$htmlDefault = renderVideoCard($videoDefaultAvatar);
assertContains('channel-avatar-placeholder', $htmlDefault, 'renderVideoCard utilise un placeholder pour l\'avatar par défaut');
assertNotContains('class="channel-avatar"', $htmlDefault, 'renderVideoCard n\'affiche pas l\'image d\'avatar par défaut');
$videoEmptyAvatar = $video;
$videoEmptyAvatar['channelAvatar'] = '';
assertContains(
'channel-avatar-placeholder',
renderVideoCard($videoEmptyAvatar),
'renderVideoCard utilise un placeholder si l\'avatar est vide'
);
// --- renderVideoCard : échappement XSS (SEC-1) ---------------------------------
$maliciousTitle = '"><img src=x onerror=alert(1)><script>alert(2)</script>';
$maliciousChannel = '<svg onload=alert(3)>';
$maliciousThumbnail = 'https://videos.example/x.jpg" onerror="alert(4)';
$maliciousAvatar = 'https://videos.example/a.png\' onerror=\'alert(5)';
$htmlXss = renderVideoCard([
'id' => $maliciousTitle,
'title' => $maliciousTitle,
'thumbnail' => $maliciousThumbnail,
'duration' => 60,
'channel' => $maliciousChannel,
'channelAvatar' => $maliciousAvatar,
'views' => 1,
'date' => date('Y-m-d H:i:s'),
]);
assertNotContains($maliciousTitle, $htmlXss, 'renderVideoCard ne ressort pas le titre brut');
assertNotContains($maliciousChannel, $htmlXss, 'renderVideoCard ne ressort pas la chaîne brute');
assertNotContains($maliciousThumbnail, $htmlXss, 'renderVideoCard ne ressort pas la vignette brute');
assertNotContains($maliciousAvatar, $htmlXss, 'renderVideoCard ne ressort pas l\'avatar brut');
assertNotContains('<script>', $htmlXss, 'renderVideoCard ne génère aucune balise script injectée');
assertNotContains('onerror="', $htmlXss, 'renderVideoCard ne laisse passer aucun attribut d\'événement actif');
assertContains(
htmlspecialchars($maliciousTitle, ENT_QUOTES, 'UTF-8'),
$htmlXss,
'renderVideoCard affiche le titre échappé'
);
assertContains(
htmlspecialchars($maliciousChannel, ENT_QUOTES, 'UTF-8'),
$htmlXss,
'renderVideoCard affiche la chaîne échappée'
);
// L'échappement ENT_QUOTES doit aussi protéger les attributs à apostrophes
$htmlAttr = renderVideoCard([
'id' => 'xyz',
'title' => "Titre avec 'apostrophe'",
'thumbnail' => 'https://videos.example/x.jpg',
'duration' => 10,
'channel' => 'Chaîne',
'channelAvatar' => 'img/default-avatar.png',
'views' => 0,
'date' => date('Y-m-d H:i:s'),
]);
assertContains('&#039;', $htmlAttr, 'renderVideoCard échappe les apostrophes dans les attributs');
assertNotContains("'apostrophe'", $htmlAttr, 'renderVideoCard ne laisse pas d\'apostrophe brute');
// --- renderVideoCard : données incomplètes --------------------------------------
$htmlMinimal = renderVideoCard(['id' => 'solo-1']);
assertContains('class="video-card"', $htmlMinimal, 'renderVideoCard tolère une vidéo quasi vide');
assertContains('data-video-id="solo-1"', $htmlMinimal, 'renderVideoCard conserve l\'id minimal');
assertContains('channel-avatar-placeholder', $htmlMinimal, 'renderVideoCard met un placeholder sans avatar');