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

+19
View File
@@ -85,6 +85,25 @@ final class SingleFileInstallerTest extends TestCase
self::assertFileDoesNotExist(\dirname($this->target).'/entwischt.txt');
}
public function testTheInstallerKeyIsWrittenForTheWebInstaller(): void
{
// install.php legt den Schlüssel dort an, wo der Installer ihn sucht (ADR 116).
$key = kant_install_key($this->target);
self::assertMatchesRegularExpression('/^[a-f0-9]{32}$/', $key);
self::assertSame($key, trim((string) file_get_contents($this->target.'/var/install.key')));
self::assertSame($key, kant_install_key($this->target), 'Ein zweiter Aufruf liest denselben Schlüssel.');
}
public function testTheReleaseCarriesItsLockFiles(): void
{
// Die Sperrdateien liegen im Repository und gleichen ihrer Vorlage (ADR 116).
$root = \dirname(__DIR__);
self::assertFileEquals($root.'/config/schutz/stamm.htaccess', $root.'/.htaccess');
foreach (['bin', 'config', 'migrations', 'plugins', 'src', 'templates'] as $ordner) {
self::assertFileEquals($root.'/config/schutz/sperren.htaccess', $root.'/'.$ordner.'/.htaccess', $ordner);
}
}
public function testThePreflightNamesWhatIsMissing(): void
{
$checks = kant_checks($this->target);