Paillasse/portail/tests/Feature/PolitiqueSecuriteContenuTest.php

60 lines
2.6 KiB
PHP
Raw Normal View History

<?php
use App\Models\Eleve;
function politique($reponse): array
{
return collect(explode(';', $reponse->headers->get('Content-Security-Policy')))
->mapWithKeys(function (string $d) {
$morceaux = preg_split('/\s+/', trim($d), 2);
return [$morceaux[0] => $morceaux[1] ?? ''];
})->all();
}
test("les pages de l'application ont une CSP stricte : pas de script en ligne sans le nonce", function () {
$eleve = Eleve::factory()->create();
$jeton = $eleve->demarreNouvelleSession();
$reponse = $this->withSession(['jeton_session_eleve' => $jeton])->actingAs($eleve, 'eleve')->get(route('eleve'));
$csp = politique($reponse);
expect($csp['script-src'])->toMatch("/^'self' 'nonce-[A-Za-z0-9+\\/=]+'$/")
->and($csp['script-src'])->not->toContain('unsafe-inline')
->and($csp['object-src'])->toBe("'none'")
->and($csp['frame-ancestors'])->toBe("'none'");
});
test('la page de connexion est stricte aussi, la vitrine tolère les scripts en ligne', function () {
expect(politique($this->get(route('login')))['script-src'])->not->toContain('unsafe-inline')
->and(politique($this->get(route('accueil')))['script-src'])->toContain("'unsafe-inline'");
});
test("chaque script de la page d'inscription porte le nonce de l'en-tête", function () {
$reponse = $this->get(route('register'));
preg_match("/'nonce-([^']+)'/", $reponse->headers->get('Content-Security-Policy'), $m);
preg_match_all('/<script\b[^>]*>/', $reponse->getContent(), $scripts);
expect($scripts[0])->not->toBeEmpty();
foreach ($scripts[0] as $balise) {
expect($balise)->toContain("nonce=\"{$m[1]}\"");
}
});
test('le rapport de violation est accepté sans jeton CSRF', function () {
$this->call('POST', route('csp.rapport'), [], [], [], ['CONTENT_TYPE' => 'application/csp-report'],
json_encode(['csp-report' => ['document-uri' => 'https://syllabuslab.fr/eleve?x=1', 'violated-directive' => 'script-src', 'blocked-uri' => 'inline']]))
->assertNoContent();
});
test("les pages de l'application ne chargent aucune ressource externe", function () {
$eleve = Eleve::factory()->create();
$jeton = $eleve->demarreNouvelleSession();
foreach ([route('login'), route('register')] as $url) {
expect($this->get($url)->getContent())->not->toMatch('#(src|href)="https?://(?!localhost)#', $url);
}
$page = $this->withSession(['jeton_session_eleve' => $jeton])->actingAs($eleve, 'eleve')->get(route('eleve'))->getContent();
expect($page)->not->toMatch('#(src|href)="https?://(?!localhost)[^"]*\.(js|css|woff2?)#');
});