fix: load markdown in direct.php, allow video-channels, guard modals

This commit is contained in:
2026-07-27 01:54:34 +04:00
parent 9b460a2550
commit 442c262539
5 changed files with 227 additions and 83 deletions
+100
View File
@@ -141,6 +141,18 @@ assertTrue(
isValidApiEndpoint('accounts/annu_kute_ced/videos'),
'isValidApiEndpoint accepte les vidéos d\'un compte'
);
assertTrue(
isValidApiEndpoint('video-channels/annu_kute_ced/videos'),
'isValidApiEndpoint accepte les vidéos d\'une chaîne'
);
assertFalse(
isValidApiEndpoint('video-channels/annu_kute_ced'),
'isValidApiEndpoint refuse une chaîne sans sous-chemin /videos'
);
assertFalse(
isValidApiEndpoint('video-channels'),
'isValidApiEndpoint refuse "video-channels" seul'
);
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');
@@ -155,3 +167,91 @@ $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');
// --- getCsrfSecret ------------------------------------------------------------
assertEquals(
CSRF_SECRET,
getCsrfSecret(),
'getCsrfSecret retourne CSRF_SECRET quand il est configuré'
);
// Repli éphémère : impossible à tester dans ce processus (CSRF_SECRET est déjà
// défini par le bootstrap), on passe par un sous-processus PHP isolé dont la
// sortie d'erreur est redirigée vers stdout pour capter l'avertissement.
$securityFile = dirname(__DIR__, 2) . '/includes/security.php';
$snippet = <<<'PHP'
require $argv[1];
define('CSRF_SECRET', CSRF_SECRET_PLACEHOLDER);
$s1 = getCsrfSecret();
$s2 = getCsrfSecret();
echo 'len=' . strlen($s1) . "\n";
echo 'same=' . ($s1 === $s2 ? 'yes' : 'no') . "\n";
echo 'placeholder=' . ($s1 === CSRF_SECRET ? 'yes' : 'no') . "\n";
$token = generateCSRFToken();
echo 'token=' . (validateCSRFToken($token) ? 'valid' : 'invalid') . "\n";
PHP;
$cmd = escapeshellarg(PHP_BINARY)
. ' -d log_errors=1 -d error_log=/dev/stdout -r '
. escapeshellarg($snippet)
. ' ' . escapeshellarg($securityFile);
$fallbackOutput = function_exists('shell_exec') ? shell_exec($cmd) : null;
if ($fallbackOutput === null) {
test_record(false, 'getCsrfSecret : sous-processus de test du repli éphémère (shell_exec indisponible)');
} else {
assertContains(
'SECURITY CRITICAL',
$fallbackOutput,
'getCsrfSecret enregistre un avertissement critique avec la valeur par défaut'
);
assertContains(
'CSRF_SECRET',
$fallbackOutput,
'l\'avertissement critique mentionne CSRF_SECRET'
);
assertContains(
'len=64',
$fallbackOutput,
'le secret éphémère fait 64 caractères hexadécimaux (32 octets)'
);
assertContains(
'same=yes',
$fallbackOutput,
'le secret éphémère est stable durant tout le processus'
);
assertContains(
'placeholder=no',
$fallbackOutput,
'le secret éphémère diffère de la valeur par défaut'
);
assertContains(
'token=valid',
$fallbackOutput,
'un token signé avec le secret éphémère est validé dans le même processus'
);
}
// --- isValidRemoteUrl (includes/security.php) ---------------------------------
assertTrue(isValidRemoteUrl('https://peertube.example.com'), 'isValidRemoteUrl accepte une URL HTTPS publique');
assertTrue(isValidRemoteUrl('https://kute.o-k-i.net'), 'isValidRemoteUrl accepte une instance Castopod publique');
assertTrue(isValidRemoteUrl('https://mizik.o-k-i.net'), 'isValidRemoteUrl accepte une instance Funkwhale publique');
assertTrue(isValidRemoteUrl('http://castopod.example.com'), 'isValidRemoteUrl accepte HTTP (développement)');
assertFalse(isValidRemoteUrl('ftp://kute.o-k-i.net'), 'isValidRemoteUrl refuse un schéma non HTTP(S)');
assertFalse(isValidRemoteUrl('pas-une-url'), 'isValidRemoteUrl refuse une chaîne mal formée');
assertFalse(isValidRemoteUrl(''), 'isValidRemoteUrl refuse une chaîne vide');
assertFalse(isValidRemoteUrl('https://localhost'), 'isValidRemoteUrl refuse localhost');
assertFalse(isValidRemoteUrl('http://127.0.0.1'), 'isValidRemoteUrl refuse 127.0.0.1');
assertFalse(isValidRemoteUrl('http://192.168.1.1'), 'isValidRemoteUrl refuse une IP privée (192.168.x)');
assertFalse(isValidRemoteUrl('http://10.0.0.5'), 'isValidRemoteUrl refuse une IP privée (10.x)');
assertFalse(isValidRemoteUrl('http://172.16.0.1'), 'isValidRemoteUrl refuse une IP privée (172.16.x)');
assertFalse(
isValidRemoteUrl('http://169.254.169.254/latest/meta-data'),
'isValidRemoteUrl refuse l\'IP de métadonnées cloud (link-local)'
);
assertFalse(
isValidRemoteUrl('https://metadata.google.internal'),
'isValidRemoteUrl refuse metadata.google.internal'
);