Cloudflare Turnstile optional, Formular-Fehlerseite repariert
Turnstile laesst sich in den Einstellungen einschalten: aus, bis beide Schluessel eingetragen sind; die Sicherheitsrichtlinie oeffnet sich nur auf Seiten mit Formular, und nur fuer challenges.cloudflare.com. Antwortet Cloudflare nicht, gilt die Absendung als bestanden - ein Ausfall dort darf keine Kundenanfrage kosten. Die drei eingebauten Pruefungen ohne Dritte bleiben davon unberuehrt. Dabei gefunden: wer ein Pflichtfeld vergass, bekam die Seite ohne Gestaltung zurueck. Der Formularcode baute sie selbst ein zweites Mal auf und liess Theme, Blockstile und Schriften weg. Jetzt geht auch dieser Weg durch den Seitencontroller, die Meldungen reisen als Anfrageattribut mit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a2dfd0f796
commit
e0d06ac44c
11 files changed
+311
-44
No files matched your search
@@ -7,6 +7,7 @@ use App\Content\CustomCode;
|
||||
use App\Content\DemoContent;
|
||||
use App\Content\DocumentValidator;
|
||||
use App\Content\FontLibrary;
|
||||
use App\Content\FormRepository;
|
||||
use App\Content\MediaLibrary;
|
||||
use App\Content\MenuRepository;
|
||||
use App\Content\PageRepository;
|
||||
@@ -20,6 +21,7 @@ use App\Content\StyleBundle;
|
||||
use App\Content\StyleCompiler;
|
||||
use App\Content\StyleSchema;
|
||||
use App\Content\Theme;
|
||||
use App\Content\Turnstile;
|
||||
use App\Domain\InvalidDocument;
|
||||
use App\Domain\InvalidMenu;
|
||||
use App\Domain\InvalidPage;
|
||||
@@ -879,6 +881,49 @@ final class CmsTest extends TestCase
|
||||
self::assertStringContainsString('Nächstes Projekt', $rand);
|
||||
}
|
||||
|
||||
public function testTheCloudflareCheckIsOffUntilItIsSetUp(): void
|
||||
{
|
||||
// Umut: „bei Formularen hätte ich gerne optional Cloudflare." Optional heißt: ohne
|
||||
// Schlüssel steht nichts im Formular, und die Sicherheitsrichtlinie bleibt so eng wie
|
||||
// vorher - eine Website ohne Spamwelle bindet keinen Dritten ein.
|
||||
$turnstile = new Turnstile($this->site);
|
||||
self::assertFalse($turnstile->isActive());
|
||||
// Ohne eingerichtete Prüfung gilt jede Absendung als bestanden.
|
||||
self::assertTrue($turnstile->passed(null, '127.0.0.1'));
|
||||
|
||||
// Eingeschaltet ohne Schlüssel wäre ein Formular, das jede Anfrage ablehnt.
|
||||
try {
|
||||
$this->site->save(['country' => 'DE', 'homePath' => 'home', 'turnstile' => '1']);
|
||||
self::fail('Ohne Schlüssel darf die Prüfung nicht angehen.');
|
||||
} catch (InvalidPage) {
|
||||
}
|
||||
|
||||
$this->site->save([
|
||||
'country' => 'DE', 'homePath' => 'home', 'turnstile' => '1',
|
||||
'turnstileSiteKey' => '0xAAAAAAAAAAAAAAAAAAAAAA', 'turnstileSecret' => '0xBBBBBBBBBBBBBBBBBBBBBB',
|
||||
]);
|
||||
self::assertTrue((new Turnstile($this->site))->isActive());
|
||||
// Ein fehlender Nachweis wird abgelehnt, ohne dass jemand gefragt werden muss.
|
||||
self::assertFalse((new Turnstile($this->site))->passed('', '127.0.0.1'));
|
||||
|
||||
(new FormRepository($this->db))->save('kontakt', 'Kontakt', [
|
||||
['name' => 'email', 'type' => 'email', 'label' => 'E-Mail', 'required' => true],
|
||||
], ['recipient' => 'info@kant-it.de', 'subject' => 'Anfrage', 'replyToField' => 'email', 'success' => 'Danke.'], 0);
|
||||
$document = DemoContent::starter('Kontakt');
|
||||
$document['schemaVersion'] = 8;
|
||||
$document['blocks'][] = ['id' => 'formblock', 'type' => 'form', 'props' => ['form' => 'kontakt']];
|
||||
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
|
||||
$antwort = $this->request('/');
|
||||
$html = $antwort->getContent();
|
||||
self::assertStringContainsString('data-sitekey="0xAAAAAAAAAAAAAAAAAAAAAA"', $html);
|
||||
// Ohne Skript kann man es nicht absenden - das steht dann auch da.
|
||||
self::assertStringContainsString('<noscript>', $html);
|
||||
// Skript und Rahmen von Cloudflare sind erlaubt, aber nichts sonst.
|
||||
$csp = (string) $antwort->headers->get('Content-Security-Policy');
|
||||
self::assertStringContainsString("script-src 'self' https://challenges.cloudflare.com", $csp);
|
||||
self::assertStringContainsString('frame-src https://challenges.cloudflare.com', $csp);
|
||||
}
|
||||
|
||||
public function testEveryPublicAnswerCarriesItsProtectiveHeaders(): void
|
||||
{
|
||||
// Diese Kopfzeilen fehlen lautlos, wenn jemand eine Antwort an der Stelle vorbeibaut, die
|
||||
|
||||
@@ -313,6 +313,11 @@ final class EditorSecurityTest extends WebTestCase
|
||||
// Each message belongs to its field and is announced as such.
|
||||
self::assertSame('formblock-email-error', $crawler->filter('#formblock-email')->attr('aria-describedby'));
|
||||
self::assertSame('true', $crawler->filter('#formblock-email')->attr('aria-invalid'));
|
||||
// Und die Seite sieht aus wie die Seite: Gestaltung, Kopf- und Fussbereich sind da.
|
||||
// Vorher baute der Formularcode die Seite selbst noch einmal auf und liess dabei Theme,
|
||||
// Blockstile und Schriften weg - wer ein Pflichtfeld vergass, sah eine nackte Seite.
|
||||
self::assertStringContainsString('<style>', $html, 'Die Gestaltung fehlt auf der Fehlerseite.');
|
||||
self::assertStringContainsString('.kant{font-family', $html);
|
||||
self::assertCount(1, $crawler->filter('[role=alert]'));
|
||||
// The entries survive the rejection.
|
||||
self::assertSame('keine-adresse', $crawler->filter('#formblock-email')->attr('value'));
|
||||
|
||||
Reference in new issue
Block a user