Weisse Seite beheben: Swagger lokal ausliefern, Startseite ergänzen

Zwei Fehler, beide erst im Browser sichtbar:

1. /docs blieb weiss. FastAPI holt Swagger von cdn.jsdelivr.net, und die
   in Phase 3 eingeführte CSP (script-src 'self') blockiert das zu Recht.
   Die Dateien liegen jetzt unter app/static/swagger/ im Repository - das
   funktioniert auch ohne Internet und ruft keinen fremden Server auf, aus
   demselben Grund, aus dem die Kantone-App ihre Schriften lokal ausliefert.
   Das Favicon holte FastAPI ebenfalls von aussen; auch das ist jetzt lokal.

2. Die Wurzel / lieferte ein nacktes 404-JSON - im Browser sieht das aus
   wie eine kaputte Anwendung. Jetzt eine schlichte Platzhalter-Seite, bis
   die Galerie in Phase 4 steht.

Ausserdem beim Testen aufgefallen: eine lokale .env-Datei reicht
ADMIN_PASSWORD auch dann herein, wenn die Umgebungsvariable gelöscht wird -
bei pydantic-settings haben Umgebungsvariablen Vorrang, ein leerer Wert
überstimmt die Datei, ein Löschen nicht. Der Test setzt jetzt leer statt zu
löschen; im Betrieb ist das dieselbe Falle.

Der neue Test prüft, dass /docs keinen einzigen Verweis auf einen fremden
Host enthält - genau das würde sonst unbemerkt wieder hereinrutschen.

61 Tests, alle grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
This commit is contained in:
2026-08-29 23:17:42 +02:00
co-authored by Claude Opus 5
parent 99ba74330c
commit 01cd0a73e8
5 changed files with 105 additions and 5 deletions
+37 -2
View File
@@ -40,8 +40,15 @@ def test_falsches_passwort(gast):
def test_ohne_passwort_ist_der_bereich_gesperrt(gast, monkeypatch, kategorie_id):
"""Ohne hinterlegtes Passwort bleibt gesperrt statt offen zu stehen."""
monkeypatch.delenv("ADMIN_PASSWORD", raising=False)
"""Ohne hinterlegtes Passwort bleibt gesperrt statt offen zu stehen.
Leerer Wert statt delenv: Umgebungsvariablen haben bei pydantic-settings
Vorrang vor der .env-Datei. Ein blosses Löschen brächte hier gar nichts,
weil eine lokale .env den Wert sonst wieder hereinreicht - dieselbe
Falle, die auch im Betrieb zuschlagen kann.
"""
monkeypatch.setenv("ADMIN_PASSWORD", "")
monkeypatch.setenv("ADMIN_PASSWORD_HASH", "")
einstellungen.cache_clear()
security.hash_zwischenspeicher_leeren()
@@ -168,3 +175,31 @@ def test_robots_txt_sperrt_suchmaschinen(gast):
antwort = gast.get("/robots.txt")
assert antwort.status_code == 200
assert "Disallow: /" in antwort.text
def test_startseite_antwortet(gast):
"""Ohne eigene Route lieferte die Wurzel ein nacktes 404-JSON - im
Browser sieht das aus wie eine kaputte Anwendung."""
antwort = gast.get("/")
assert antwort.status_code == 200
assert "Kinderkleider-Börse" in antwort.text
def test_swagger_laedt_nichts_von_fremden_servern(gast):
"""Die mitgelieferte /docs-Seite holt Swagger von cdn.jsdelivr.net.
Die CSP (script-src 'self') blockiert das zu Recht - die Seite bliebe
weiss. Ausserdem ginge dabei die IP jedes Besuchers an einen Dritten.
"""
import re
antwort = gast.get("/docs")
assert antwort.status_code == 200
# Kein einziger Verweis auf einen anderen Host
fremde = re.findall(r'(?:src|href)="(https?:)?//[^"]+"', antwort.text)
assert not fremde, f"lädt von aussen: {fremde}"
assert "/static/swagger/swagger-ui-bundle.js" in antwort.text
# Und die Dateien sind auch wirklich da
assert gast.get("/static/swagger/swagger-ui-bundle.js").status_code == 200
assert gast.get("/static/swagger/swagger-ui.css").status_code == 200