test(php): add native unit tests for security, cache, format, markdown
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests unitaires pour includes/security.php
|
||||
* (et les validateurs d'URL de includes/config.php)
|
||||
*/
|
||||
|
||||
// --- validateVideoId --------------------------------------------------------
|
||||
|
||||
assertEquals(
|
||||
'9c5de94d-8e1a-4b3c-9d2e-1234567890ab',
|
||||
validateVideoId('9c5de94d-8e1a-4b3c-9d2e-1234567890ab'),
|
||||
'validateVideoId accepte un UUID valide'
|
||||
);
|
||||
assertEquals(
|
||||
'9C5DE94D-8E1A-4B3C-9D2E-1234567890AB',
|
||||
validateVideoId('9C5DE94D-8E1A-4B3C-9D2E-1234567890AB'),
|
||||
'validateVideoId accepte un UUID en majuscules'
|
||||
);
|
||||
assertEquals(
|
||||
'9c5de94d-8e1a-4b3c-9d2e-1234567890ab',
|
||||
validateVideoId(' 9c5de94d-8e1a-4b3c-9d2e-1234567890ab '),
|
||||
'validateVideoId nettoie les espaces autour de l\'UUID'
|
||||
);
|
||||
assertFalse(validateVideoId(''), 'validateVideoId refuse une chaîne vide');
|
||||
assertFalse(validateVideoId('pas-un-uuid'), 'validateVideoId refuse une chaîne quelconque');
|
||||
assertFalse(
|
||||
validateVideoId('9c5de94d-8e1a-4b3c-1234567890ab'),
|
||||
'validateVideoId refuse un UUID tronqué'
|
||||
);
|
||||
|
||||
// --- validateSearchQuery ----------------------------------------------------
|
||||
|
||||
assertEquals('indépendance', validateSearchQuery('indépendance'), 'validateSearchQuery conserve les accents');
|
||||
assertEquals(
|
||||
'script',
|
||||
validateSearchQuery('<script>'),
|
||||
'validateSearchQuery supprime les chevrons'
|
||||
);
|
||||
assertEquals(
|
||||
'recherche btest/b',
|
||||
validateSearchQuery('recherche <b>test</b>'),
|
||||
'validateSearchQuery supprime les caractères dangereux <>"\''
|
||||
);
|
||||
assertEquals('terme', validateSearchQuery(' terme '), 'validateSearchQuery nettoie les espaces');
|
||||
assertFalse(validateSearchQuery(''), 'validateSearchQuery refuse une chaîne vide');
|
||||
assertFalse(
|
||||
validateSearchQuery(str_repeat('a', 201)),
|
||||
'validateSearchQuery refuse plus de 200 caractères'
|
||||
);
|
||||
assertEquals(
|
||||
str_repeat('a', 200),
|
||||
validateSearchQuery(str_repeat('a', 200)),
|
||||
'validateSearchQuery accepte exactement 200 caractères'
|
||||
);
|
||||
|
||||
// --- validateCategoryId -----------------------------------------------------
|
||||
|
||||
assertEquals(1, validateCategoryId(1), 'validateCategoryId accepte la borne basse 1');
|
||||
assertEquals(20, validateCategoryId(20), 'validateCategoryId accepte la borne haute 20');
|
||||
assertEquals(5, validateCategoryId('5'), 'validateCategoryId convertit une chaîne numérique');
|
||||
assertFalse(validateCategoryId(0), 'validateCategoryId refuse 0');
|
||||
assertFalse(validateCategoryId(21), 'validateCategoryId refuse 21');
|
||||
assertFalse(validateCategoryId(-3), 'validateCategoryId refuse un nombre négatif');
|
||||
assertFalse(validateCategoryId('abc'), 'validateCategoryId refuse une chaîne non numérique');
|
||||
|
||||
// --- validatePageNumber -----------------------------------------------------
|
||||
|
||||
assertEquals(1, validatePageNumber(0), 'validatePageNumber remonte 0 à 1');
|
||||
assertEquals(1, validatePageNumber(-5), 'validatePageNumber remonte un négatif à 1');
|
||||
assertEquals(3, validatePageNumber(3), 'validatePageNumber conserve une page valide');
|
||||
assertEquals(2, validatePageNumber('2'), 'validatePageNumber convertit une chaîne numérique');
|
||||
assertEquals(1, validatePageNumber('abc'), 'validatePageNumber ramène une chaîne non numérique à 1');
|
||||
|
||||
// --- generateCSRFToken / validateCSRFToken ----------------------------------
|
||||
|
||||
$token = generateCSRFToken();
|
||||
assertContains(':', $token, 'generateCSRFToken produit le format "timestamp:hash"');
|
||||
|
||||
[$timestamp, $hash] = explode(':', $token, 2);
|
||||
assertTrue(ctype_digit($timestamp), 'generateCSRFToken : la partie timestamp est numérique');
|
||||
assertEquals(
|
||||
hash_hmac('sha256', $timestamp, CSRF_SECRET),
|
||||
$hash,
|
||||
'generateCSRFToken : le hash est un HMAC-SHA256 du timestamp avec CSRF_SECRET'
|
||||
);
|
||||
assertTrue(validateCSRFToken($token), 'validateCSRFToken accepte un token fraîchement généré');
|
||||
|
||||
assertFalse(validateCSRFToken(''), 'validateCSRFToken refuse une chaîne vide');
|
||||
assertFalse(validateCSRFToken('sans-deux-points'), 'validateCSRFToken refuse un token sans séparateur');
|
||||
assertFalse(validateCSRFToken('abc:' . $hash), 'validateCSRFToken refuse un timestamp non numérique');
|
||||
assertFalse(
|
||||
validateCSRFToken($timestamp . ':' . hash_hmac('sha256', $timestamp, 'autre-secret')),
|
||||
'validateCSRFToken refuse un hash signé avec un autre secret'
|
||||
);
|
||||
assertFalse(
|
||||
validateCSRFToken($timestamp . ':0' . $hash),
|
||||
'validateCSRFToken refuse un hash modifié'
|
||||
);
|
||||
|
||||
// Token expiré : horodatage d'il y a 2 heures (limite : 1 heure)
|
||||
$oldTimestamp = (string) (time() - 7200);
|
||||
$oldToken = $oldTimestamp . ':' . hash_hmac('sha256', $oldTimestamp, CSRF_SECRET);
|
||||
assertFalse(validateCSRFToken($oldToken), 'validateCSRFToken refuse un token expiré (> 1 heure)');
|
||||
|
||||
// Timestamp trop loin dans le futur (l'écart absolu est également plafonné)
|
||||
$futureTimestamp = (string) (time() + 7200);
|
||||
$futureToken = $futureTimestamp . ':' . hash_hmac('sha256', $futureTimestamp, CSRF_SECRET);
|
||||
assertFalse(validateCSRFToken($futureToken), 'validateCSRFToken refuse un timestamp trop futur');
|
||||
|
||||
// --- isValidPeerTubeUrl (includes/config.php) -------------------------------
|
||||
|
||||
assertTrue(isValidPeerTubeUrl('https://peertube.example.com'), 'isValidPeerTubeUrl accepte une URL HTTPS publique');
|
||||
assertTrue(isValidPeerTubeUrl('http://peertube.example.com'), 'isValidPeerTubeUrl accepte HTTP (développement)');
|
||||
assertTrue(isValidPeerTubeUrl('https://8.8.8.8'), 'isValidPeerTubeUrl accepte une IP publique');
|
||||
assertFalse(isValidPeerTubeUrl('ftp://example.com'), 'isValidPeerTubeUrl refuse un schéma non HTTP(S)');
|
||||
assertFalse(isValidPeerTubeUrl('pas-une-url'), 'isValidPeerTubeUrl refuse une chaîne mal formée');
|
||||
assertFalse(isValidPeerTubeUrl('https://localhost'), 'isValidPeerTubeUrl refuse localhost');
|
||||
assertFalse(isValidPeerTubeUrl('http://127.0.0.1'), 'isValidPeerTubeUrl refuse 127.0.0.1');
|
||||
assertFalse(isValidPeerTubeUrl('http://192.168.1.1'), 'isValidPeerTubeUrl refuse une IP privée');
|
||||
assertFalse(isValidPeerTubeUrl('http://10.0.0.5'), 'isValidPeerTubeUrl refuse une IP privée (10.x)');
|
||||
assertFalse(
|
||||
isValidPeerTubeUrl('https://metadata.google.internal'),
|
||||
'isValidPeerTubeUrl refuse metadata.google.internal'
|
||||
);
|
||||
|
||||
// --- isValidApiEndpoint (includes/config.php) -------------------------------
|
||||
|
||||
assertTrue(isValidApiEndpoint('videos'), 'isValidApiEndpoint accepte "videos"');
|
||||
assertTrue(isValidApiEndpoint('videos/categories'), 'isValidApiEndpoint accepte "videos/categories"');
|
||||
assertTrue(isValidApiEndpoint('search/videos'), 'isValidApiEndpoint accepte "search/videos"');
|
||||
assertTrue(isValidApiEndpoint('accounts'), 'isValidApiEndpoint accepte "accounts"');
|
||||
assertTrue(
|
||||
isValidApiEndpoint('videos/9c5de94d-8e1a-4b3c-9d2e-1234567890ab'),
|
||||
'isValidApiEndpoint accepte un endpoint vidéo dynamique'
|
||||
);
|
||||
assertTrue(
|
||||
isValidApiEndpoint('videos/abc/comment-threads'),
|
||||
'isValidApiEndpoint accepte les fils de commentaires'
|
||||
);
|
||||
assertTrue(
|
||||
isValidApiEndpoint('accounts/annu_kute_ced/videos'),
|
||||
'isValidApiEndpoint accepte les vidéos d\'un compte'
|
||||
);
|
||||
assertFalse(isValidApiEndpoint('../config'), 'isValidApiEndpoint refuse le path traversal');
|
||||
assertFalse(isValidApiEndpoint('videos/../x'), 'isValidApiEndpoint refuse ".." dans le chemin');
|
||||
assertFalse(isValidApiEndpoint('videos//categories'), 'isValidApiEndpoint refuse un double slash');
|
||||
assertFalse(isValidApiEndpoint('wp-admin'), 'isValidApiEndpoint refuse un endpoint hors liste blanche');
|
||||
assertFalse(isValidApiEndpoint('videos.json'), 'isValidApiEndpoint refuse les caractères non autorisés (.)');
|
||||
assertFalse(isValidApiEndpoint('videos?count=1'), 'isValidApiEndpoint refuse une query string');
|
||||
assertFalse(isValidApiEndpoint(''), 'isValidApiEndpoint refuse une chaîne vide');
|
||||
|
||||
// --- getCspNonce ------------------------------------------------------------
|
||||
|
||||
$nonce = getCspNonce();
|
||||
assertTrue(is_string($nonce) && $nonce !== '', 'getCspNonce retourne une chaîne non vide');
|
||||
assertEquals(16, strlen(base64_decode($nonce, true)), 'getCspNonce est un base64 de 16 octets aléatoires');
|
||||
assertEquals($nonce, getCspNonce(), 'getCspNonce retourne le même nonce durant toute la requête');
|
||||
Reference in New Issue
Block a user