Files
annu-kute-ced/tests/php/structured-data-test.php
T

123 lines
5.5 KiB
PHP
Raw Normal View History

<?php
/**
* Tests unitaires pour includes/structured-data.php
* (encodage JSON-LD anti-injection et construction d'URL de confiance)
*/
// --- JSON-LD : neutralisation de "</script>" ---------------------------------
$maliciousTitle = 'Vidéo </script><script>alert("xss")</script> test';
$videoData = [
'thumbnailPath' => '/static/thumbnails/test.jpg',
];
$video = [
'id' => '9c5de94d-8e1a-4b3c-9d2e-1234567890ab',
'title' => $maliciousTitle,
'description' => 'Une description',
'duration' => 120,
'channel' => 'Chaîne </script>',
'views' => 42,
'date' => '2024-01-15 10:00:00',
'tags' => ['test'],
];
$jsonLd = generateVideoObjectJsonLd($videoData, $video);
assertNotContains('</script>', $jsonLd, 'generateVideoObjectJsonLd ne reproduit pas "</script>" en clair');
// L'équivalent encodé de "</script>" (séquences \u00XX via JSON_HEX_TAG)
// doit apparaître à la place de la balise en clair
$encodedClosingTag = trim(json_encode('</script>', JSON_UNESCAPED_SLASHES | JSON_HEX_TAG), '"');
assertContains(
$encodedClosingTag,
$jsonLd,
'generateVideoObjectJsonLd encode "<" et ">" en séquences unicode (JSON_HEX_TAG)'
);
$decoded = json_decode($jsonLd, true);
assertTrue(is_array($decoded), 'Le JSON-LD VideoObject reste un JSON valide');
assertEquals(
$maliciousTitle,
$decoded['name'],
'Le titre malveillant est préservé à l\'identique après décodage JSON'
);
// Les autres générateurs JSON-LD bénéficient des mêmes drapeaux d'encodage
$websiteLd = generateWebSiteJsonLd();
assertNotContains('</script>', $websiteLd, 'generateWebSiteJsonLd ne contient pas "</script>"');
assertTrue(json_decode($websiteLd, true) !== null, 'generateWebSiteJsonLd produit un JSON valide');
$breadcrumbLd = generateBreadcrumbJsonLd([
['name' => 'Accueil', 'url' => 'https://test.local'],
['name' => 'Page </script>', 'url' => 'https://test.local/page'],
]);
assertNotContains('</script>', $breadcrumbLd, 'generateBreadcrumbJsonLd neutralise "</script>" dans un nom de fil d\'Ariane');
$collectionLd = generateVideoCollectionJsonLd('Collection </script>', 'Description', [$video], 'https://test.local/col');
assertNotContains('</script>', $collectionLd, 'generateVideoCollectionJsonLd neutralise "</script>" dans le nom de collection');
$podcastLd = generatePodcastJsonLd([
[
'title' => 'Épisode </script>',
'link' => 'https://podcast.example/ep1',
'pubDate' => '2024-01-01',
],
]);
assertNotContains('</script>', $podcastLd, 'generatePodcastJsonLd neutralise "</script>" dans un titre d\'épisode');
assertTrue(json_decode($podcastLd, true) !== null, 'generatePodcastJsonLd produit un JSON valide');
// --- isValidAppHostName -------------------------------------------------------
assertTrue(isValidAppHostName('example.com'), 'isValidAppHostName accepte un nom de domaine');
assertTrue(isValidAppHostName('test.local'), 'isValidAppHostName accepte un domaine local');
assertTrue(isValidAppHostName('localhost'), 'isValidAppHostName accepte localhost');
assertTrue(isValidAppHostName('127.0.0.1:8080'), 'isValidAppHostName accepte une IPv4 avec port');
assertTrue(isValidAppHostName('sub.example-site.com'), 'isValidAppHostName accepte sous-domaines et tirets');
assertFalse(isValidAppHostName(''), 'isValidAppHostName refuse une chaîne vide');
assertFalse(isValidAppHostName('evil.com"><script>'), 'isValidAppHostName refuse guillemets et chevrons');
assertFalse(isValidAppHostName("evil.com\r\nX-Injected: 1"), 'isValidAppHostName refuse les CRLF (injection d\'en-tête)');
assertFalse(isValidAppHostName('http://evil.com'), 'isValidAppHostName refuse une URL avec schéma');
assertFalse(isValidAppHostName('evil com'), 'isValidAppHostName refuse les espaces');
assertFalse(isValidAppHostName('evil.com/path'), 'isValidAppHostName refuse un chemin');
// --- getBaseUrl / getAppHostName : hôte de confiance --------------------------
// Un en-tête Host malveillant ne doit pas influencer l'URL de base
$_SERVER['HTTP_HOST'] = 'evil.example"><script>alert(1)</script>';
$_SERVER['HTTPS'] = 'on';
assertEquals(
'https://' . APP_HOST_NAME,
getBaseUrl(),
'getBaseUrl utilise APP_HOST_NAME et ignore un HTTP_HOST malveillant'
);
assertNotContains('evil.example', getBaseUrl(), 'getBaseUrl ne reflète pas l\'en-tête Host');
assertEquals(APP_HOST_NAME, getAppHostName(), 'getAppHostName retourne APP_HOST_NAME validé');
unset($_SERVER['HTTPS']);
assertEquals('http://' . APP_HOST_NAME, getBaseUrl(), 'getBaseUrl retombe en http sans HTTPS');
// --- getCurrentUrl : REQUEST_URI assaini --------------------------------------
$_SERVER['REQUEST_URI'] = '/video.php?id=abc"><meta http-equiv="refresh" content="0;url=https://phishing.example">';
$currentUrl = getCurrentUrl();
assertNotContains('"', $currentUrl, 'getCurrentUrl supprime les guillemets de REQUEST_URI');
assertNotContains('<', $currentUrl, 'getCurrentUrl supprime les chevrons de REQUEST_URI');
assertNotContains(' ', $currentUrl, 'getCurrentUrl supprime les espaces de REQUEST_URI');
assertEquals(
0,
strpos($currentUrl, 'http://' . APP_HOST_NAME . '/video.php?id=abc'),
'getCurrentUrl préfixe par l\'hôte validé et conserve le chemin'
);
$_SERVER['REQUEST_URI'] = 'sans-slash-initial';
assertEquals(
'http://' . APP_HOST_NAME . '/sans-slash-initial',
getCurrentUrl(),
'getCurrentUrl ajoute le slash initial manquant'
);
// Nettoyage des superglobales modifiées pour ne pas impacter d'autres tests
unset($_SERVER['HTTPS']);
$_SERVER['HTTP_HOST'] = APP_HOST_NAME;
$_SERVER['REQUEST_URI'] = '/';