security: SRI Font Awesome, CSP restrictions, remove X-XSS-Protection, rate limiting

This commit is contained in:
2026-07-27 09:15:20 +04:00
parent ddd82fdff6
commit 7cbb7334f0
11 changed files with 186 additions and 135 deletions
+82
View File
@@ -255,3 +255,85 @@ assertFalse(
isValidRemoteUrl('https://metadata.google.internal'),
'isValidRemoteUrl refuse metadata.google.internal'
);
// --- cspOriginFromUrl ---------------------------------------------------------
assertEquals(
'https://peertube.example.com',
cspOriginFromUrl('https://peertube.example.com'),
'cspOriginFromUrl extrait l\'origine d\'une URL simple'
);
assertEquals(
'https://peertube.example.com',
cspOriginFromUrl('https://peertube.example.com/chemin/page?x=1'),
'cspOriginFromUrl ignore le chemin et la query string'
);
assertEquals(
'http://127.0.0.1',
cspOriginFromUrl('http://127.0.0.1'),
'cspOriginFromUrl conserve le schéma http'
);
assertEquals('', cspOriginFromUrl(''), 'cspOriginFromUrl refuse une chaîne vide');
assertEquals('', cspOriginFromUrl('pas-une-url'), 'cspOriginFromUrl refuse une URL mal formée');
// --- buildContentSecurityPolicy ------------------------------------------------
/**
* Extrait la valeur d'une directive CSP pour les assertions
*/
function csp_directive($csp, $name) {
if (preg_match('/(?:^| )' . preg_quote($name, '/') . ' ([^;]+)/', $csp, $m)) {
return $m[1];
}
return null;
}
$testNonce = 'nonce-de-test';
$csp = buildContentSecurityPolicy($testNonce);
assertContains(
"'nonce-{$testNonce}",
csp_directive($csp, 'script-src'),
'CSP : script-src porte le nonce de la requête'
);
assertContains(
"'nonce-{$testNonce}",
csp_directive($csp, 'style-src'),
'CSP : style-src porte le nonce de la requête'
);
// Domaines réellement utilisés présents
$imgSrc = csp_directive($csp, 'img-src');
$mediaSrc = csp_directive($csp, 'media-src');
assertContains(cspOriginFromUrl(PEERTUBE_URL), $imgSrc, 'CSP : img-src autorise le domaine PeerTube');
assertContains(cspOriginFromUrl(MASTODON_INSTANCE_URL), $imgSrc, 'CSP : img-src autorise le domaine Mastodon');
assertContains(cspOriginFromUrl(PEERTUBE_URL), $mediaSrc, 'CSP : media-src autorise le domaine PeerTube');
assertContains(cspOriginFromUrl(CASTOPOD_URL), $mediaSrc, 'CSP : media-src autorise le domaine Castopod');
assertContains(cspOriginFromUrl(CASTOPOD_URL), $imgSrc, 'CSP : img-src autorise les pochettes Castopod');
// Pas de joker https:/http: hors développement local (HTTP_HOST absent en CLI)
assertTrue(
preg_match('/(^|\s)https?:($|\s)/', $imgSrc) === 0,
'CSP : img-src ne contient pas de joker https: en production'
);
assertTrue(
preg_match('/(^|\s)https?:($|\s)/', $mediaSrc) === 0,
'CSP : media-src ne contient pas de joker https: en production'
);
// Directives de verrouillage toujours présentes
assertContains("object-src 'none'", $csp, 'CSP : object-src none est présent');
assertContains("frame-ancestors 'self'", $csp, 'CSP : frame-ancestors self est présent');
// En développement local, le joker HTTP(S) est réintroduit pour le contenu fédéré
$_SERVER['HTTP_HOST'] = 'localhost:8080';
$cspDev = buildContentSecurityPolicy($testNonce);
assertTrue(
preg_match('/(^|\s)https?:($|\s)/', csp_directive($cspDev, 'img-src')) === 1,
'CSP : img-src contient le joker https: en développement local'
);
assertTrue(
preg_match('/(^|\s)https?:($|\s)/', csp_directive($cspDev, 'media-src')) === 1,
'CSP : media-src contient le joker https: en développement local'
);
unset($_SERVER['HTTP_HOST']);