"""Geheimnisse schwärzen (Ausbauplan „Später“, Punkt 20, seit 26.09.2026) — für die Konfig-Sicherung in Gitea. User-Entscheid 25.09.2026: privates Repo, Geheimnisse geschwärzt. Geschwärzt wird, was nach Zugangsdaten aussieht: • private Schlüssel (PEM-Blöcke) • Werte hinter Schlüsseln wie password, secret, token, api_key, key, salt, cookie, credential (YAML, INI, .env, „- NAME=wert“ in Compose) • Passwort-Hashes (bcrypt, argon2, sha-crypt, apr1) und htpasswd-Zeilen • Zugangsdaten in Adressen (scheme://benutzer:passwort@…), Authorization-/API-Kopfzeilen in nginx • typische Token-Formate (sk-…, hf_…, gh?_…) und lange Hex-Ketten (≥ 32 Zeichen, etwa API-Schlüssel) Lieber zu viel als zu wenig: Beim Zurückholen trägt der User Geheimnisse ohnehin selbst wieder ein. Dieselben Regeln stehen im Ausführer (deploy/homelab/ausfuehrer.py, SCHWAERZEN), damit Geheimnisse den Proxmox-Host gar nicht erst verlassen; ein Test prüft, dass beide gleich schwärzen. """ import re ERSATZ = "«geschwärzt»" _WORT = (r"(?:pass(?:word|wd|phrase)?|secret|token|api[_-]?key|apikey|private[_-]?key|credential|salt|cookie|" r"auth[_-]?key|access[_-]?key|client[_-]?secret|jwt|key(?![a-z]))") REGELN: tuple[tuple[re.Pattern, str], ...] = ( (re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----.*?-----END [A-Z ]*PRIVATE KEY-----", re.DOTALL), f"-----BEGIN PRIVATE KEY-----\n{ERSATZ}\n-----END PRIVATE KEY-----"), (re.compile(rf"(?im)^([ \t]*(?:-[ \t]*)?[\"']?[\w.-]*{_WORT}[\w.-]*[\"']?[ \t]*[:=][ \t]*)(?![ \t]*$)(?!{ERSATZ})(.+)$"), rf"\g<1>{ERSATZ}"), (re.compile(r"(?im)^(\s*proxy_set_header\s+(?:authorization|x-api-key|x-auth-token|cookie)\s+)(\"[^\"]*\"|\S+?)(;?)$"), rf'\g<1>"{ERSATZ}"\g<3>'), (re.compile(r"\$(?:2[abxy]|argon2(?:id|i|d)|apr1|[156])\$[^\s\"',;]+"), ERSATZ), (re.compile(r"(?m)^([\w.@-]+):(?:\{SHA\}|\$)\S+$"), rf"\g<1>:{ERSATZ}"), (re.compile(r"(\b[a-z][a-z0-9+.-]*://[^\s:/@]+:)[^\s@/]+@"), rf"\g<1>{ERSATZ}@"), (re.compile(r"(?i)\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{8,}"), rf"\g<1> {ERSATZ}"), (re.compile(r"\b(?:sk-|hf_|gh[pousr]_)[A-Za-z0-9_-]{16,}"), ERSATZ), (re.compile(r"\b[0-9a-fA-F]{32,}\b"), ERSATZ), ) def schwaerzen(text: str) -> tuple[str, int]: """(geschwärzter Text, Zahl der Stellen).""" zahl = 0 for muster, ersatz in REGELN: text, n = muster.subn(ersatz, text) zahl += n return text, zahl