Redirect the other www spelling to SITE_ORIGIN, hold roles to the role help and fix the audit findings
SITE_ORIGIN alone decides whether the site is www or not: CanonicalHostSubscriber sends the other spelling there with 301, cms:site:origin writes the setting, the system screen shows it. The roles now match the role help (editor: drafts, publisher: live content, admin: design, settings, users, system). The installer no longer reopens on an unreachable database, the session cookie follows ADMIN_PATH, percent-encoded credentials no longer break the container, consent only accepts same-site decisions, JSON-LD is HTML-safe, own CSS stays in its block, plus the editor, updater, download and public-output findings from the full review (ADR 099, ADR 100). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
45040b2ca6
commit
7eb462ac7b
53 files changed
+1023
-122
No files matched your search
+27
-2
@@ -413,6 +413,17 @@ final class CmsTest extends TestCase
|
||||
self::assertSame('/', $response->headers->get('Location'));
|
||||
}
|
||||
|
||||
public function testTheOtherSpellingOfTheHostIsSentToSiteOrigin(): void
|
||||
{
|
||||
// SITE_ORIGIN der Tests ist https://cms.test - die www-Fassung fuehrt dorthin, mit Pfad
|
||||
// und Abfrage, bevor irgendjemand die Datenbank fragt.
|
||||
$response = $this->request('http://www.cms.test/home?a=1');
|
||||
self::assertSame(301, $response->getStatusCode());
|
||||
self::assertSame('https://cms.test/home?a=1', $response->headers->get('Location'));
|
||||
// Ein fremder Host bleibt erreichbar: eine falsch eingetragene Adresse sperrt niemanden aus.
|
||||
self::assertSame(200, $this->request('http://vorschau.hoster.test/robots.txt')->getStatusCode());
|
||||
}
|
||||
|
||||
public function testCreatedPageStaysPrivateUntilPublishedAndKeepsItsAddress(): void
|
||||
{
|
||||
$id = $this->pages->createPage('ueber-uns', null, 'page', DemoContent::starter('Über uns'));
|
||||
@@ -1431,7 +1442,7 @@ final class CmsTest extends TestCase
|
||||
$ohne = $this->request('/');
|
||||
self::assertStringNotContainsString('fonts.googleapis.com', $ohne->getContent());
|
||||
self::assertStringNotContainsString('fonts.gstatic.com', $ohne->getContent());
|
||||
self::assertStringNotContainsString('rel="preload"'." href=\"/assets/fonts", $ohne->getContent());
|
||||
self::assertStringNotContainsString('rel="preload" href="/assets/fonts', $ohne->getContent());
|
||||
|
||||
// Mit Dateien: die eigenen Dateien, und zwar **jeder** Schnitt vorab. Fehlte einer, läge
|
||||
// er erst nach dem Stylesheet vor, und die Überschrift spränge sichtbar von der
|
||||
@@ -2036,6 +2047,15 @@ final class CmsTest extends TestCase
|
||||
self::assertStringContainsString("script-src 'none'", (string) $other->headers->get('Content-Security-Policy'));
|
||||
self::assertSame(404, $this->request('/gibt-es-nicht')->getStatusCode());
|
||||
self::assertStringContainsString("script-src 'none'", (string) $this->request('/gibt-es-nicht')->headers->get('Content-Security-Policy'));
|
||||
|
||||
// Tabs im Fussbereich: die Seite selbst hat keine, braucht das Skript aber trotzdem - und
|
||||
// eine Richtlinie, die es zulaesst. Vorher blieb der Fuss eine Ankerliste mit blockiertem
|
||||
// Skript.
|
||||
$footer = $this->partials->findByKind('footer');
|
||||
$this->partials->save($footer['id'], [...$footer['document'], 'schemaVersion' => max((int) $footer['document']['schemaVersion'], 7), 'blocks' => [...$footer['document']['blocks'], ...array_slice($document['blocks'], -1)]], (int) $footer['version']);
|
||||
$withFooterTabs = $this->request('/ohne-tabs');
|
||||
self::assertStringContainsString('<script src="/assets/tabs.js?v=', $withFooterTabs->getContent());
|
||||
self::assertStringContainsString("script-src 'self'", (string) $withFooterTabs->headers->get('Content-Security-Policy'));
|
||||
}
|
||||
|
||||
public function testTabsSurviveSavingAndRejectMisplacedEntries(): void
|
||||
@@ -2075,7 +2095,11 @@ final class CmsTest extends TestCase
|
||||
$this->posts->saveMetadata($hidden, ['robots' => 'noindex'], [], [], $this->pages->findForEditor($hidden)['version']);
|
||||
$this->publishedPage('offen', 'Offen');
|
||||
$this->pages->createPage('entwurf', null, 'page', DemoContent::starter('Entwurf'));
|
||||
$this->publishedPost('ein-beitrag', 'Ein Beitrag', '2026-02-01', ['Projekte'], []);
|
||||
$post = $this->publishedPost('ein-beitrag', 'Ein Beitrag', '2026-02-01', ['Projekte'], []);
|
||||
// Neu veroeffentlicht nach einer Aenderung: lastmod ist das Datum der Fassung, nicht das
|
||||
// der ersten Veroeffentlichung.
|
||||
$edited = $this->pages->findForEditor($post);
|
||||
$this->pages->publish($post, $this->pages->saveDraft($post, $edited['document'], $edited['version']), $edited['version'] + 1);
|
||||
|
||||
$sitemap = $this->request('/sitemap.xml');
|
||||
self::assertSame(200, $sitemap->getStatusCode());
|
||||
@@ -2086,6 +2110,7 @@ final class CmsTest extends TestCase
|
||||
self::assertStringContainsString('<loc>https://cms.test/offen</loc>', $xml);
|
||||
self::assertStringContainsString('<loc>https://cms.test/beitraege</loc>', $xml);
|
||||
self::assertStringContainsString('<loc>https://cms.test/kategorie/projekte</loc>', $xml);
|
||||
self::assertStringContainsString("<loc>https://cms.test/ein-beitrag</loc>\n <lastmod>".gmdate('Y-m-d').'</lastmod>', $xml);
|
||||
// A noindex page and an unpublished page have no business in a sitemap.
|
||||
self::assertStringNotContainsString('/intern', $xml);
|
||||
self::assertStringNotContainsString('/entwurf', $xml);
|
||||
|
||||
Reference in new issue
Block a user