Automatischer Push: 19 Datei(en) geändert

- .vault-tools/plan-kopie/LIESMICH.md
- .vault-tools/plan-kopie/rot_nachweis.py
- .vault-tools/plan-kopie/welle_vergleich.py
- .vault-tools/plan-kopie/welle_vergleich_treiber.py
- .vault-tools/trading-plattform-pruefung/LIESMICH.md
- .vault-tools/trading-plattform-pruefung/angriffe.py
- .vault-tools/trading-plattform-pruefung/browserlauf.py
- .vault-tools/trading-plattform-pruefung/browserlauf_zugang.py
- .vault-tools/trading-plattform-pruefung/c_eigenlauf.py
- .vault-tools/trading-plattform-pruefung/d_eigenlauf.py
- .vault-tools/trading-plattform-pruefung/handelstag_auswertung.py
- .vault-tools/trading-plattform-pruefung/ntfy_probe_server.py
- … und 7 weitere

Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5 committed 2026-09-19 04:00:01 +02:00
1 parent 371a1f5d2a
commit 395642c480
19 files changed
+1636 -32

No files matched your search

@@ -10,14 +10,25 @@ Geltungsbereich (Herkunft jeder Erwartung: Projektvorgabe, Spec Teil 10, Abschni
Wiederholung eines TOTP-Codes, Abmeldung entwertet das Cookie, WebSocket ohne Cookie und mit
fremdem Origin abgelehnt, Bremse (429 mit Retry-After, auch fuer die richtige Anmeldung),
Anmeldeprotokoll ohne Passwort und Code, /api/paper mit Protokollpfad ausserhalb des Ordners.
Was es nicht sieht: TLS und das Secure-Merkmal (das Probe-Backend spricht HTTP), DNS-Rebinding im
echten Browser, Zeitverhalten der Passwortpruefung, alles hinter der Anmeldung ausser einem GET und
einem POST je Fall, und ob die CSP die gebaute Oberflaeche wirklich traegt (dafuer der Browser-Lauf).
- seit 2026-09-18 abhaengig vom Schema der Basis-URL (Herkunft: Projektvorgabe, Spec Teil 10 Teil B
und trading/dashboard/sicherheit.py: secure=tls am Cookie, Strict-Transport-Security nur mit TLS):
unter https traegt das Sitzungscookie Secure und jede Antwort Strict-Transport-Security mit
positivem max-age, und eine Klartext-Anfrage an denselben Port liefert keine HTTP-Antwort 200;
unter http traegt das Cookie kein Secure und keine Antwort Strict-Transport-Security.
Was es nicht sieht: ob das Zertifikat taugt (es vertraut der mit --ca genannten Datei, bei einem
selbstsignierten Zertifikat also dem Zertifikat selbst), Protokollversionen und Chiffren von TLS,
TLS-Terminierung vor dem Dashboard (dann sieht die Anwendung HTTP und setzt weder Secure noch HSTS
-- das meldet dieser Lauf unter https zu Recht als Befund), DNS-Rebinding im echten Browser,
Zeitverhalten der Passwortpruefung, alles hinter der Anmeldung ausser einem GET und einem POST je
Fall, und ob die CSP die gebaute Oberflaeche wirklich traegt (dafuer der Browser-Lauf).
Regressionsnachweis: gegen das alte Probe-Backend ohne Anmeldung (start_probe_backend.py) muss der
Lauf mit Exit 2 enden und die Schutzfaelle einzeln rot zeigen.
Lauf mit Exit 2 enden und die Schutzfaelle einzeln rot zeigen. Fuer die https-Faelle: gegen ein
TLS-Backend, dessen sicherheit.py weder Secure noch HSTS setzt (Mutant im Ziel), muessen genau
diese Faelle rot sein.
Aufruf (in WSL, Venv des Projekts):
python angriffe.py <basis-url> <ablageordner des probe-backends> [--ohne-bremse]
python angriffe.py <basis-url> <ablageordner des probe-backends> [--ohne-bremse] [--ca <pem>]
--ca nennt die Zertifikatsdatei, der bei https vertraut wird (selbstsigniertes Zertifikat).
Exit 0 ohne Befund, 2 mit Befund, 3 wenn eine Voraussetzung fehlt (auch das ist ein Befund).
"""
@@ -25,6 +36,8 @@ import base64
import hashlib
import hmac
import json
import os
import re
import struct
import sys
import time
@@ -66,6 +79,17 @@ def main() -> int:
mit_bremse = "--ohne-bremse" not in sys.argv[3:]
host = basis.split("://", 1)[1]
origin = basis
tls = basis.startswith("https://")
if "--ca" in sys.argv[3:]:
stelle = sys.argv.index("--ca")
if stelle + 1 >= len(sys.argv) or not Path(sys.argv[stelle + 1]).is_file():
print("VORAUSSETZUNG FEHLT: --ca braucht eine vorhandene Zertifikatsdatei")
return 3
# requests liest REQUESTS_CA_BUNDLE, der WebSocket-Client baut seinen Kontext ueber
# ssl.create_default_context(), das SSL_CERT_FILE liest. Pruefen statt abschalten.
os.environ["REQUESTS_CA_BUNDLE"] = sys.argv[stelle + 1]
os.environ["SSL_CERT_FILE"] = sys.argv[stelle + 1]
zugang_json = ablage / "zugang.json"
if not zugang_json.is_file():
@@ -133,6 +157,25 @@ def main() -> int:
pruefe(f"{pfad}: nosniff", kopf.get("x-content-type-options") == "nosniff", str(kopf.get("x-content-type-options")))
pruefe(f"{pfad}: Referrer-Policy", kopf.get("referrer-policy") == "no-referrer", str(kopf.get("referrer-policy")))
pruefe(f"{pfad}: kein Server-Kopf", "server" not in kopf, str(kopf.get("server")))
for pfad in ("/", "/api/gesund", "/api/uebersicht"):
hsts = requests.get(f"{basis}{pfad}", timeout=5).headers.get("strict-transport-security")
if tls:
treffer = re.fullmatch(r"max-age=(\d+)(;.*)?", hsts or "")
pruefe(
f"{pfad}: Strict-Transport-Security mit positivem max-age (HTTPS)",
treffer is not None and int(treffer.group(1)) > 0,
str(hsts),
)
else:
pruefe(f"{pfad}: kein Strict-Transport-Security (HTTP)", hsts is None, str(hsts))
if tls:
klartext = "http://" + host
try:
offen = requests.get(f"{klartext}/api/gesund", timeout=5)
ergebnis_klartext = f"HTTP {offen.status_code}"
except requests.RequestException as ausnahme:
ergebnis_klartext = f"keine Antwort: {type(ausnahme).__name__}"
pruefe("Klartext-Anfrage an den TLS-Port liefert kein 200", ergebnis_klartext != "HTTP 200", ergebnis_klartext)
api_kopf = requests.get(f"{basis}/api/gesund", timeout=5).headers
pruefe("/api/gesund: Cache-Control no-store", api_kopf.get("cache-control") == "no-store", str(api_kopf.get("cache-control")))
@@ -169,7 +212,11 @@ def main() -> int:
pruefe("Cookie HttpOnly", "httponly" in klein, setzen[-80:])
pruefe("Cookie SameSite=Strict", "samesite=strict" in klein, setzen[-80:])
pruefe("Cookie Path=/", "path=/" in klein, setzen[-80:])
pruefe("Cookie ohne Secure (HTTP)", "secure" not in klein, setzen[-80:])
merkmale = [teil.strip() for teil in klein.split(";")]
if tls:
pruefe("Cookie mit Secure (HTTPS)", "secure" in merkmale, setzen[-80:])
else:
pruefe("Cookie ohne Secure (HTTP)", "secure" not in merkmale, setzen[-80:])
wieder = requests.post(f"{basis}/api/anmeldung", json={**rumpf, "code": code}, headers=kopf, timeout=10)
pruefe("derselbe Code ein zweites Mal 401", wieder.status_code == 401, f"HTTP {wieder.status_code}")