From c971e0acb424c1b50975069f0ee8d31ce540029b Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Sun, 9 Aug 2026 20:25:39 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20#149:=20Regressionstest=20f=C3=BCr=20refl?= =?UTF-8?q?ektiertes=20XSS=20=C3=BCber=20Kontonummer-Feld?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Changes.md | 5 ++ tests/84_BookingStoreReflectedXssTest.php | 73 +++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tests/84_BookingStoreReflectedXssTest.php diff --git a/Changes.md b/Changes.md index b1a2369..fc9bef7 100644 --- a/Changes.md +++ b/Changes.md @@ -2,6 +2,11 @@ --- +## 2026-08-09 – Regressionstest: Reflektiertes XSS über Kontonummer-Freitextfeld (#149) + +- `BookingController::store()` schreibt eine nicht auflösbare Freitext-Kontonummer (`kategorie_kontonummer`/`zahlungs_kontonummer`) ungefiltert in `$errors[...]`. Die Ausgabestelle in `src/Views/belege/detail.php:419` escaped Schlüssel und Wert bereits seit Fix #132 (Commit 6489819) konsequent mit `$h()` – der in #149 gemeldete Bug war zum Zeitpunkt der Meldung durch die dortige Änderung bereits geschlossen, nur ohne eigene Testabdeckung für diesen speziellen Eingabepfad. +- **`tests/84_BookingStoreReflectedXssTest.php`** (neu): Rendert `belege/detail.php` mit einem `'; + + // Nachbau der Fehlerauflösung aus BookingController::store() (Zeile 100-105): + // eine nicht auflösbare Freitext-Kontonummer landet unescaped in $errors. + $errors = [ + 'kategorie_konto_id' => "Kontonummer '$payload' nicht gefunden.", + ]; + + // BookingController::renderBelegDetail() nur bis zum Content-Fragment + // nachgebaut (belege/detail.php), ohne layout/base.php – ein zweiter + // voller Seiten-Render im selben Testlauf würde an der Funktions- + // deklaration in layout/nav.php scheitern (siehe Test 65). + $beleg = \Steuerkram\Models\Beleg::find($belegId); + $accounts = \Steuerkram\Models\Account::all(['aktiv' => 1]); + + $controller = new \Steuerkram\Controllers\BookingController(); + $renderView = new \ReflectionMethod($controller, 'renderView'); + $renderView->setAccessible(true); + $html = $renderView->invoke($controller, 'belege/detail', [ + 'beleg' => $beleg, + 'bookings' => \Steuerkram\Models\Booking::forBeleg($belegId), + 'integrity' => \Steuerkram\Models\Beleg::verifyIntegrity($belegId), + 'accounts' => $accounts, + 'assets' => \Steuerkram\Models\Asset::all(), + 'formErrors' => $errors, + 'formData' => [], + 'bankTx' => null, + 'bankTxBrutto' => 0.0, + 'linkedBankTx' => null, + ]); + + $this->assert( + !str_contains($html, $payload), + 'Roher