Add an own-HTML block, and build the DEEG offers page on the embed block
Umut asked for a block that takes custom HTML, and for an iframe on a new DEEG offers page. Only one of those was missing. New core block "Eigenes HTML" (document schema v24): one property, the markup, up to 20.000 characters, written to the page unchanged. Changing it is reserved for administrators and compared against what is stored - on saving, on saving an area, and on restoring an older revision - exactly like an own script. <script> and <iframe> are refused with the way to the right place instead of being swallowed by the content security policy: scripts belong in the script field, frames in the "Externer Inhalt" block, because only that one asks for consent and puts the host into frame-src (ADR 106). DEEG: /angebote existed as a draft with placeholder text. It is now built like the legal pages - small caps line, large light heading, lead on the tinted band - with the IS24 result list below it and a line pointing to the portal and to the contact form. Navigation and the offers band on the home page point there. The consent question is switched on, so the frame loads only after the visitor agrees, and both the banner and the placeholder carry the site's own shapes and colours. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6b1aa50d02
commit
59a40157a4
15 files changed
+465
-8
No files matched your search
@@ -3111,6 +3111,79 @@ final class CmsTest extends TestCase
|
||||
$code->assertScriptsUnchanged($added, $before);
|
||||
}
|
||||
|
||||
public function testOwnHtmlReachesThePageUnchangedAndRefusesScriptAndFrame(): void
|
||||
{
|
||||
// Der Baustein „Eigenes HTML“ (Schema v24): Markup des Betreibers, unverändert auf der
|
||||
// Seite. Zwei Tags nimmt er nicht an, weil die Sicherheitsrichtlinie sie ohnehin nicht
|
||||
// ausführt bzw. nicht lädt - dafür gibt es das Skriptfeld und den Baustein „Externer
|
||||
// Inhalt“.
|
||||
$id = $this->publishedPage('angebote', 'Angebote');
|
||||
$document = $this->pages->findForEditor($id)['document'];
|
||||
$document['schemaVersion'] = 24;
|
||||
$document['blocks'][0]['children'][] = ['id' => 'roh', 'type' => 'html', 'props' => ['html' => '<table class="preise"><tr><td>Miete</td><td>ab 8 €/m²</td></tr></table>']];
|
||||
$revision = $this->pages->saveDraft($id, $document, $this->pages->findForEditor($id)['version']);
|
||||
$this->pages->publish($id, $revision, $this->pages->findForEditor($id)['version']);
|
||||
|
||||
$html = $this->request('/angebote')->getContent();
|
||||
self::assertStringContainsString('<div id="roh" class="block-html">', $html);
|
||||
// Unverändert heißt unverändert: keine Entschärfung, keine Umschrift.
|
||||
self::assertStringContainsString('<table class="preise"><tr><td>Miete</td><td>ab 8 €/m²</td></tr></table>', $html);
|
||||
|
||||
foreach ([
|
||||
'<script>alert(1)</script>' => 'Skript',
|
||||
'<iframe src="https://example.com"></iframe>' => 'Rahmen',
|
||||
'< script >alert(1)</script>' => 'Skript mit Leerzeichen',
|
||||
] as $markup => $fall) {
|
||||
$attempt = $document;
|
||||
$attempt['blocks'][0]['children'][2]['props']['html'] = $markup;
|
||||
try {
|
||||
$this->pages->saveDraft($id, $attempt, $this->pages->findForEditor($id)['version']);
|
||||
self::fail(sprintf('%s hätte abgewiesen werden müssen.', $fall));
|
||||
} catch (InvalidDocument) {
|
||||
}
|
||||
}
|
||||
|
||||
// Vor v24 gibt es den Baustein nicht.
|
||||
$alt = $document;
|
||||
$alt['schemaVersion'] = 23;
|
||||
try {
|
||||
$this->pages->saveDraft($id, $alt, $this->pages->findForEditor($id)['version']);
|
||||
self::fail('Eigenes HTML vor Schema v24 hätte abgewiesen werden müssen.');
|
||||
} catch (InvalidDocument) {
|
||||
}
|
||||
}
|
||||
|
||||
public function testOwnHtmlChangesAreComparedAgainstWhatIsStored(): void
|
||||
{
|
||||
// Dieselbe Rolle wie beim eigenen Skript: ändern darf es nur ein Administrator, und
|
||||
// geprüft wird gegen den gespeicherten Stand - ein Redakteur kann die Seite also weiter
|
||||
// speichern, ohne das HTML anfassen zu können.
|
||||
$code = new CustomCode();
|
||||
$stored = DemoContent::starter('Mit HTML');
|
||||
$stored['schemaVersion'] = 24;
|
||||
$stored['blocks'][0]['children'][] = ['id' => 'roh', 'type' => 'html', 'props' => ['html' => '<p>Original</p>']];
|
||||
|
||||
$unchanged = $stored;
|
||||
$unchanged['title'] = 'Anderer Titel';
|
||||
$code->assertHtmlUnchanged($unchanged, $stored);
|
||||
|
||||
foreach (['geändert' => '<p>Fremd</p>', 'geleert' => ''] as $fall => $markup) {
|
||||
$attempt = $stored;
|
||||
$attempt['blocks'][0]['children'][2]['props']['html'] = $markup;
|
||||
try {
|
||||
$code->assertHtmlUnchanged($attempt, $stored);
|
||||
self::fail(sprintf('HTML %s: hätte abgewiesen werden müssen.', $fall));
|
||||
} catch (InvalidDocument) {
|
||||
}
|
||||
}
|
||||
|
||||
// Und neu hinzufügen ebenso wenig.
|
||||
$ohne = DemoContent::starter('Ohne HTML');
|
||||
$ohne['schemaVersion'] = 24;
|
||||
$this->expectException(InvalidDocument::class);
|
||||
$code->assertHtmlUnchanged($stored, $ohne);
|
||||
}
|
||||
|
||||
/** Turns on the consent question and publishes a privacy page it can point to. */
|
||||
private function askConsent(array $groups = ['external']): void
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user