Automatischer Push: 14 Datei(en) geändert
- .vault-tools/trading-plattform-pruefung/LIESMICH.md - .vault-tools/trading-plattform-pruefung/bremse_echtlauf.py - .vault-tools/trading-plattform-pruefung/browser_w.py - .vault-tools/trading-plattform-pruefung/generalprobe_echtlauf.py - .vault-tools/trading-plattform-pruefung/ha_aktiv_setzen.py - .vault-tools/trading-plattform-pruefung/handelstag_auswertung.py - .vault-tools/trading-plattform-pruefung/hausmelder_echtlauf.py - .vault-tools/trading-plattform-pruefung/killswitch_simulation.py - .vault-tools/trading-plattform-pruefung/killswitch_simulation_mutanten.py - .vault-tools/trading-plattform-pruefung/latenz_auswertung.py - .vault-tools/trading-plattform-pruefung/s1_alpaca_lesen.py - .vault-tools/trading-plattform-pruefung/s5_echtlauf.py - … und 2 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:
1 parent
246af19683
commit
593acdd122
14 files changed
+2090
-5
No files matched your search
@@ -0,0 +1,216 @@
|
||||
"""Gegenmessung zu Paket S5 gegen eine laufende Probe-Instanz: dieselben Angriffe, die der Sicherheitspruefer gemessen hat.
|
||||
|
||||
Koordinator-Pruefcode (WSL). Gemessen wird gegen 127.0.0.1:<port>; die Instanz laeuft aus einer Kopie mit
|
||||
Broker-Attrappe, Wegwerf-Register und Wegwerf-Zugang. Nichts davon beruehrt den Betrieb.
|
||||
Aufruf: python s5_echtlauf.py <port> <ablage-der-probe> <code-baum>
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
PORT = int(sys.argv[1])
|
||||
ABLAGE = Path(sys.argv[2])
|
||||
CODE = Path(sys.argv[3])
|
||||
BASIS = f"http://127.0.0.1:{PORT}"
|
||||
KOPF = {"Origin": BASIS}
|
||||
z = json.loads((ABLAGE / "zugang.json").read_text(encoding="utf-8"))
|
||||
BEN, PW, GEH = z["benutzer"], z["passwort"], z["geheimnis"]
|
||||
befunde = []
|
||||
|
||||
|
||||
def code(versatz=0):
|
||||
s = base64.b32decode(GEH)
|
||||
mac = hmac.new(s, struct.pack(">Q", int(time.time()) // 30 + versatz), hashlib.sha1).digest()
|
||||
v = mac[-1] & 0x0F
|
||||
return f"{(struct.unpack('>I', mac[v:v + 4])[0] & 0x7FFFFFFF) % 1_000_000:06d}"
|
||||
|
||||
|
||||
def melde_an():
|
||||
s = requests.Session()
|
||||
s.trust_env = False
|
||||
a = s.post(f"{BASIS}/api/anmeldung", json={"benutzer": BEN, "passwort": PW, "code": code()}, headers=KOPF, timeout=60)
|
||||
return s, a.status_code
|
||||
|
||||
|
||||
def naechster_schritt():
|
||||
time.sleep(30.5 - (time.time() % 30))
|
||||
|
||||
|
||||
def server_pid():
|
||||
aus = subprocess.run(["pgrep", "-f", "start_probe_w.py"], capture_output=True, text=True).stdout.split()
|
||||
return int(aus[0]) if aus else None
|
||||
|
||||
|
||||
def speicher_mib(pid):
|
||||
werte = {}
|
||||
for zeile in Path(f"/proc/{pid}/status").read_text().splitlines():
|
||||
if zeile.startswith(("VmRSS", "VmHWM")):
|
||||
name, wert = zeile.split(":")
|
||||
werte[name] = int(wert.split()[0]) / 1024
|
||||
return werte
|
||||
|
||||
|
||||
def pruefe(text, ok):
|
||||
print((" OK " if ok else " BEFUND ") + text)
|
||||
if not ok:
|
||||
befunde.append(text)
|
||||
|
||||
|
||||
pid = server_pid()
|
||||
print(f"Server-PID {pid}, Speicher zu Beginn {speicher_mib(pid)}")
|
||||
|
||||
print("== 1: 422 spiegelt die Eingabe nicht mehr")
|
||||
lang = "P" * 300
|
||||
a = requests.post(f"{BASIS}/api/anmeldung", json={"benutzer": BEN, "passwort": lang, "code": "123456"}, headers=KOPF, timeout=10)
|
||||
pruefe(f"HTTP {a.status_code}, Passwort im Koerper: {lang in a.text}", a.status_code == 422 and lang not in a.text)
|
||||
print(f" Koerper: {a.text[:200]}")
|
||||
|
||||
print("== 2: Rumpf 512 MiB mit content-length, unangemeldet")
|
||||
vor = speicher_mib(pid)
|
||||
t0 = time.perf_counter()
|
||||
verbindung = http.client.HTTPConnection("127.0.0.1", PORT, timeout=60)
|
||||
verbindung.putrequest("POST", "/api/anmeldung")
|
||||
verbindung.putheader("Origin", BASIS)
|
||||
verbindung.putheader("Content-Type", "application/json")
|
||||
verbindung.putheader("Content-Length", str(512 * 1024 * 1024))
|
||||
verbindung.endheaders()
|
||||
gesendet = 0
|
||||
status = None
|
||||
try:
|
||||
block = b"x" * (1024 * 1024)
|
||||
for _ in range(512):
|
||||
verbindung.send(block)
|
||||
gesendet += len(block)
|
||||
except OSError as ausnahme:
|
||||
print(f" Senden brach nach {gesendet / 1024 / 1024:.0f} MiB ab: {type(ausnahme).__name__}")
|
||||
try:
|
||||
antwort = verbindung.getresponse()
|
||||
status = antwort.status
|
||||
antwort.read()
|
||||
except Exception as ausnahme: # noqa: BLE001
|
||||
print(f" Antwort nicht lesbar: {type(ausnahme).__name__}")
|
||||
verbindung.close()
|
||||
nach = speicher_mib(pid)
|
||||
print(f" Status {status}, {time.perf_counter() - t0:.2f} s, gesendet {gesendet / 1024 / 1024:.0f} MiB, Speicher {vor} -> {nach}")
|
||||
pruefe("413 und Spitzenspeicher waechst um weniger als 50 MiB", status == 413 and nach["VmHWM"] - vor["VmHWM"] < 50)
|
||||
|
||||
print("== 3: Rumpf ohne content-length (chunked), unangemeldet")
|
||||
vor = speicher_mib(pid)
|
||||
t0 = time.perf_counter()
|
||||
verbindung = http.client.HTTPConnection("127.0.0.1", PORT, timeout=60)
|
||||
verbindung.putrequest("POST", "/api/anmeldung")
|
||||
verbindung.putheader("Origin", BASIS)
|
||||
verbindung.putheader("Content-Type", "application/json")
|
||||
verbindung.putheader("Transfer-Encoding", "chunked")
|
||||
verbindung.endheaders()
|
||||
gesendet = 0
|
||||
status = None
|
||||
try:
|
||||
block = b"y" * (256 * 1024)
|
||||
for _ in range(800):
|
||||
verbindung.send(hex(len(block))[2:].encode() + b"\r\n" + block + b"\r\n")
|
||||
gesendet += len(block)
|
||||
verbindung.send(b"0\r\n\r\n")
|
||||
except OSError as ausnahme:
|
||||
print(f" Senden brach nach {gesendet / 1024 / 1024:.1f} MiB ab: {type(ausnahme).__name__}")
|
||||
try:
|
||||
antwort = verbindung.getresponse()
|
||||
status = antwort.status
|
||||
antwort.read()
|
||||
except Exception as ausnahme: # noqa: BLE001
|
||||
print(f" Antwort nicht lesbar: {type(ausnahme).__name__}")
|
||||
verbindung.close()
|
||||
nach = speicher_mib(pid)
|
||||
print(f" Status {status}, {time.perf_counter() - t0:.2f} s, gesendet {gesendet / 1024 / 1024:.1f} MiB, Speicher {vor} -> {nach}")
|
||||
pruefe("413 (oder Abbruch der Verbindung) und Spitzenspeicher waechst um weniger als 50 MiB",
|
||||
status in (413, None) and nach["VmHWM"] - vor["VmHWM"] < 50)
|
||||
|
||||
print("== 4: Killswitch unter 40 parallelen teuren Kerzenabrufen")
|
||||
sitz, st = melde_an()
|
||||
print(f" Anmeldung HTTP {st}")
|
||||
cookie = "; ".join(f"{c.name}={c.value}" for c in sitz.cookies)
|
||||
ds = sitz.get(f"{BASIS}/api/markt/datensaetze", timeout=120).json().get("datensaetze", [])
|
||||
intraday = [d for d in ds if d.get("intraday")]
|
||||
if not intraday:
|
||||
print(" kein Intraday-Datensatz im Katalog -- Lastmessung nicht moeglich")
|
||||
befunde.append("Lastmessung nicht moeglich")
|
||||
else:
|
||||
bt = intraday[0]["bar_type"]
|
||||
teuer = f"{BASIS}/api/markt/kerzen?bar_type={bt}&aufloesung_min=60&anzahl=3000"
|
||||
t0 = time.perf_counter()
|
||||
einzel = requests.get(teuer, headers={"Cookie": cookie}, timeout=600)
|
||||
print(f" ein einzelner teurer Abruf: HTTP {einzel.status_code}, {time.perf_counter() - t0:.1f} s")
|
||||
t0 = time.perf_counter()
|
||||
sitz.post(f"{BASIS}/api/killswitch/anhalten", json={"grund": "grundlinie"}, headers=KOPF, timeout=60)
|
||||
print(f" Grundlinie POST /api/killswitch/anhalten ohne Last: {time.perf_counter() - t0:.2f} s")
|
||||
laeuft = [True]
|
||||
messung = {"gesund": [], "anhalten": []}
|
||||
|
||||
def miss():
|
||||
while laeuft[0]:
|
||||
t = time.perf_counter()
|
||||
try:
|
||||
requests.get(f"{BASIS}/api/gesund", timeout=300)
|
||||
messung["gesund"].append(time.perf_counter() - t)
|
||||
except Exception: # noqa: BLE001
|
||||
messung["gesund"].append(-1.0)
|
||||
t = time.perf_counter()
|
||||
try:
|
||||
r = sitz.post(f"{BASIS}/api/killswitch/anhalten", json={"grund": "lastprobe"}, headers=KOPF, timeout=300)
|
||||
messung["anhalten"].append((time.perf_counter() - t, r.status_code))
|
||||
except Exception: # noqa: BLE001
|
||||
messung["anhalten"].append((-1.0, 0))
|
||||
time.sleep(0.5)
|
||||
|
||||
faden = threading.Thread(target=miss, daemon=True)
|
||||
faden.start()
|
||||
t0 = time.perf_counter()
|
||||
with ThreadPoolExecutor(max_workers=40) as pool:
|
||||
codes = list(pool.map(lambda _: requests.get(teuer, headers={"Cookie": cookie}, timeout=900).status_code, range(40)))
|
||||
dauer = time.perf_counter() - t0
|
||||
laeuft[0] = False
|
||||
faden.join(timeout=310)
|
||||
zeiten = [w for w, _ in messung["anhalten"]]
|
||||
print(f" 40 Kerzenabrufe: {dauer:.1f} s, Status {sorted(set(codes))}")
|
||||
print(f" anhalten waehrend der Last: n={len(zeiten)}, max {max(zeiten):.2f} s, Status {sorted({s for _, s in messung['anhalten']})}")
|
||||
print(f" gesund waehrend der Last: n={len(messung['gesund'])}, max {max(messung['gesund']):.2f} s")
|
||||
pruefe("Killswitch antwortet unter Last in unter 5 s (vorher gemessen: 118 s)", 0 <= max(zeiten) < 5.0)
|
||||
|
||||
print("== 5: ueberlanger Grund landet nicht im Register")
|
||||
db = ABLAGE / "registry.db"
|
||||
vorher = db.stat().st_size
|
||||
a = sitz.post(f"{BASIS}/api/killswitch/anhalten", json={"grund": "G" * (1024 * 1024)}, headers=KOPF, timeout=120)
|
||||
print(f" grund mit 1 MiB -> HTTP {a.status_code}, registry.db {vorher} -> {db.stat().st_size} Bytes")
|
||||
pruefe("abgelehnt (413 oder 422), Register waechst nicht", a.status_code in (413, 422) and db.stat().st_size - vorher < 65536)
|
||||
|
||||
print("== 6: Passwortwechsel beendet die Sitzung")
|
||||
pruefe(f"Sitzung gilt vor dem Wechsel (HTTP {sitz.get(f'{BASIS}/api/uebersicht', timeout=60).status_code})", True)
|
||||
sys.path.insert(0, str(CODE))
|
||||
os.chdir(CODE)
|
||||
from trading.dashboard.zugang import Benutzer, lies_benutzer, schreibe_benutzer, setze_passwort # noqa: E402
|
||||
|
||||
bestand = lies_benutzer(ABLAGE / "zugang.toml")
|
||||
bestand[BEN] = Benutzer(name=BEN, passwort=setze_passwort("ein-neues-passwort-2026"), totp_geheimnis=bestand[BEN].totp_geheimnis)
|
||||
schreibe_benutzer(ABLAGE / "zugang.toml", bestand)
|
||||
time.sleep(1.0)
|
||||
danach = sitz.get(f"{BASIS}/api/uebersicht", timeout=60).status_code
|
||||
pruefe(f"alte Sitzung nach dem Passwortwechsel: HTTP {danach}", danach == 401)
|
||||
halt = sitz.post(f"{BASIS}/api/killswitch/anhalten", json={"grund": "nach-wechsel"}, headers=KOPF, timeout=60).status_code
|
||||
pruefe(f"Killswitch mit der alten Sitzung: HTTP {halt}", halt == 401)
|
||||
|
||||
print(f"Speicher am Ende {speicher_mib(pid)}")
|
||||
print(f"{len(befunde)} Befund(e): {befunde}")
|
||||
sys.exit(2 if befunde else 0)
|
||||
Reference in new issue
Block a user