"""Dass die Absicherung im Betrieb auch wirklich anspringt. 🔑 **Warum das einen eigenen Test braucht.** Die Einstellungen hängen an ``DEBUG=0``, und in der Entwicklung *und in den Tests* ist ``DJANGO_DEBUG`` standardmäßig ``1``. Die ganze Absicherung läuft also in keinem einzigen normalen Testlauf — sie stünde richtig in der Datei und wäre nie geprüft. Genau die Sorte Sicherheitsnetz, die stumm kaputt ist. Deshalb hier die Einstellungsdatei einmal frisch mit ``DJANGO_DEBUG=0`` einlesen und nachsehen, was dabei herauskommt. """ from __future__ import annotations import importlib import os from unittest import mock from django.test import SimpleTestCase def betriebs_einstellungen(): """Lädt ``vogelalbum.settings`` neu, als liefe es im Betrieb.""" umgebung = { "DJANGO_DEBUG": "0", # Im Betrieb ist ein eigener Schlüssel Pflicht, sonst bricht der # Import ab — hier einer, der nur in diesem Test lebt. "DJANGO_SECRET_KEY": "nur-fuer-diesen-test-" + "x" * 40, } with mock.patch.dict(os.environ, umgebung): modul = importlib.import_module("vogelalbum.settings") return importlib.reload(modul) class BetriebsEinstellungenTests(SimpleTestCase): @classmethod def setUpClass(cls): super().setUpClass() cls.betrieb = betriebs_einstellungen() @classmethod def tearDownClass(cls): # ⚠️ Zurückladen, sonst sieht jeder nachfolgende Test die # Betriebsfassung — und scheitert an Dingen, die mit ihm nichts zu # tun haben. import vogelalbum.settings importlib.reload(vogelalbum.settings) super().tearDownClass() def test_debug_ist_aus(self): self.assertFalse(self.betrieb.DEBUG) def test_cookies_nur_ueber_https(self): """🔴 Der Kern: Die Umleitung auf https kommt zu spät. Traefik antwortet mit 307 — da hat der Browser das Cookie aber schon mitgeschickt. Erst ``Secure`` hält ihn davon ab. """ self.assertTrue(self.betrieb.SESSION_COOKIE_SECURE) self.assertTrue(self.betrieb.CSRF_COOKIE_SECURE) def test_hsts_ist_gesetzt(self): self.assertGreaterEqual(self.betrieb.SECURE_HSTS_SECONDS, 31536000) def test_hsts_nicht_fuer_fremde_namen(self): """Keine Zusagen für Namen, die es nicht gibt, und kein preload. ``includeSubDomains`` gilt auch für Namen, die noch niemand angelegt hat, und ``preload`` nimmt einem die Entscheidung aus der Hand: Aus fremden Listen kommt man nur langsam wieder heraus. """ self.assertFalse(self.betrieb.SECURE_HSTS_INCLUDE_SUBDOMAINS) self.assertFalse(self.betrieb.SECURE_HSTS_PRELOAD) def test_proxy_kopf_wird_ausgewertet(self): """Ohne das baut build_absolute_uri() http://-Adressen.""" self.assertEqual( self.betrieb.SECURE_PROXY_SSL_HEADER, ("HTTP_X_FORWARDED_PROTO", "https"), ) def test_referrer_bleibt_im_haus(self): self.assertEqual(self.betrieb.SECURE_REFERRER_POLICY, "same-origin") def test_in_der_entwicklung_gilt_nichts_davon(self): """⚠️ Sonst kann sich auf http://localhost niemand mehr anmelden. Ein ``Secure``-Cookie wird über einfaches HTTP gar nicht gesetzt — die Anmeldung schlüge fehl, und die Ursache wäre ausgerechnet die Einstellung, die Sicherheit bringen soll. """ from django.conf import settings # ⚠️ ``settings.DEBUG`` taugt hier NICHT als Probe: Der Testläufer # setzt es grundsätzlich auf False, egal was die Datei gerechnet hat. # Entscheidend ist, was der ``if not DEBUG``-Block beim IMPORT gesehen # hat — und das war ``DJANGO_DEBUG=1``. Also die Flaggen prüfen. self.assertFalse(getattr(settings, "SESSION_COOKIE_SECURE", False)) self.assertFalse(getattr(settings, "CSRF_COOKIE_SECURE", False)) self.assertEqual(getattr(settings, "SECURE_HSTS_SECONDS", 0), 0)