Harden KantSites after the security review

- The installer only opens with a per-installation key that install.php
  creates and shows; the key is deleted with the first account.
- Lock files keep .env, the database, backups and logs out of reach when a
  domain points at the project folder instead of public/.
- Linked sections render by their own schema version, so an old section
  cannot carry unfiltered HTML into a newer page.
- Editors no longer move, trash or change areas and post data of published
  pages; a scheduled publication goes live with the revision that was
  scheduled (migration 20261012000100).
- Form stamps are bound to their form, IPv6 senders count per /64, and a
  flood per form or overall stops storing with a visible message.
- The consent redirect rejects control characters, update targets must look
  like 1.2.3, and own CSS cannot reach outside its block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5.5 committed 2026-10-12 14:38:25 +02:00
1 parent 14e7a3b310
commit ad7c333215
43 files changed
+810 -53

No files matched your search

+17 -2
View File
@@ -141,8 +141,15 @@ final class CustomCode
if (1 !== preg_match('/^[a-zA-Z0-9_-]{1,64}$/D', $blockId)) {
return '';
}
// Gespeichert wurde nur, was beim Speichern galt. Ist eine Regel inzwischen strenger
// (ADR 116), fällt das CSS dieses einen Blocks weg - die Seite selbst bleibt erreichbar.
try {
$rules = $this->parse($css);
} catch (InvalidDocument) {
return '';
}
$out = '';
foreach ($this->parse($css) as $suffix => $declarations) {
foreach ($rules as $suffix => $declarations) {
if ('' === $declarations) {
continue;
}
@@ -193,6 +200,13 @@ final class CustomCode
if (1 !== preg_match('/^[a-zA-Z0-9 .:>+~_\[\]="\'-]*$/D', $suffix)) {
throw new InvalidDocument(sprintf('„&%s“ ist als verschachtelter Selektor nicht erlaubt.', $suffix));
}
// Geschwister des Elements selbst (`& ~ *`, `& + p`) liegen außerhalb des Blocks - damit
// ließe sich die ganze Seite umgestalten. Innerhalb des Blocks bleiben `~` und `+`
// erlaubt. Und Anführungszeichen paarweise: ein offener String verschluckte sonst
// alle folgenden Regeln der Seite (ADR 116).
if (1 === preg_match('/^\s*[~+]/', $suffix) || 0 !== substr_count($suffix, '"') % 2 || 0 !== substr_count($suffix, "'") % 2) {
throw new InvalidDocument(sprintf('„&%s“ reicht über den Block hinaus und ist deshalb nicht erlaubt.', $suffix));
}
// Zusammenfuegen statt ersetzen: zweimal derselbe Selektor ist in CSS eine Kaskade,
// nicht ein Widerruf. Vorher gewann die letzte Regel, und alles, was nur in der
// ersten stand, verschwand ohne Meldung - gefunden am Projektraster von EB Solar,
@@ -247,7 +261,8 @@ final class CustomCode
$out[] = $property.':'.$value;
continue;
}
if ('' === $value || 1 !== preg_match('/^[a-zA-Z0-9 ,.%#()\/:_+*-]{1,300}$/D', $value)) {
// Kein Kommentaranfang und -ende: ein offener Kommentar verschluckte die Regeln danach.
if ('' === $value || 1 !== preg_match('/^[a-zA-Z0-9 ,.%#()\/:_+*-]{1,300}$/D', $value) || str_contains($value, '/*') || str_contains($value, '*/')) {
throw new InvalidDocument(sprintf('Der Wert von „%s“ enthält Zeichen, die in eigenem CSS nicht vorgesehen sind.', $property));
}
$out[] = $property.':'.$value;