- .claude/skills/plane-abgleich/SKILL.md - .vault-tools/geheimnis-scan/LIESMICH.md - .vault-tools/geheimnis-scan/datei_scan.py - .vault-tools/geheimnis-scan/push_scan.py Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py. Co-Authored-By: Claude Opus 5 <[email protected]>
63 lines
2.6 KiB
Python
63 lines
2.6 KiB
Python
"""Durchsucht die ausgehenden Commits eines Repos (Bereich a..b) nach Geheimnis-Mustern.
|
||
|
||
Ersteller: Claude (Opus 5.5) · Erstellt: 2026-10-15 · Doku: LIESMICH.md
|
||
|
||
Gezeigt werden nur hinzugefügte Zeilen. Werte, die wie Geheimnisse aussehen, nur als
|
||
Länge; dazu Datei und Muster. Aufruf: python -I -X utf8 push_scan.py <repo> <bereich>
|
||
Bereich ohne Basis (neuer Zweig auf dem Remote): "<zweig> --not --remotes=origin".
|
||
"""
|
||
import re
|
||
import subprocess
|
||
import sys
|
||
from collections import Counter, defaultdict
|
||
|
||
repo, bereich = sys.argv[1], sys.argv[2:]
|
||
log = subprocess.run(
|
||
["git", "-C", repo, "log", "-p", "--no-color", "--format=COMMIT %h", *bereich],
|
||
capture_output=True, text=True, encoding="utf-8", errors="replace", check=True,
|
||
).stdout
|
||
|
||
# Muster mit Wert: nur Länge ausgeben
|
||
GEHEIM = {
|
||
"zuweisung": re.compile(r"(?i)\b(pass(?:wor[dt])?|kennwort|pwd|token|secret|api[_-]?key|client[_-]?secret|private[_-]?key)\b[\"']?\s*[:=]\s*[\"']([^\"'\s$<{]{8,})[\"']"),
|
||
"privater_schluessel": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
|
||
"gitea_github_token": re.compile(r"\b(?:ghp_[A-Za-z0-9]{30,}|gh[ousr]_[A-Za-z0-9]{30,}|glpat-[A-Za-z0-9_-]{20,})\b"),
|
||
"url_mit_zugang": re.compile(r"https?://[^/\s:@]+:[^/\s@]+@"),
|
||
"hash_shadow": re.compile(r"\$(?:6|5|y|2[aby])\$[^\s:\"']{20,}"),
|
||
}
|
||
# Muster ohne Geheimnis: Zählung je Wert
|
||
ZAEHLEN = {
|
||
"private_ipv4": re.compile(r"\b(?:10\.\d{1,3}|192\.168|172\.(?:1[6-9]|2\d|3[01]))\.\d{1,3}\.\d{1,3}\b"),
|
||
"firma": re.compile(r"\b[A-ZÄÖÜ][\w&.-]+(?:\s+[A-ZÄÖÜ][\w&.-]+){0,2}\s+(?:GmbH|AG|KG|UG|e\.V\.)\b"),
|
||
}
|
||
|
||
datei = "?"
|
||
commits = 0
|
||
geheim = defaultdict(list)
|
||
zaehl = {k: Counter() for k in ZAEHLEN}
|
||
for zeile in log.splitlines():
|
||
if zeile.startswith("COMMIT "):
|
||
commits += 1
|
||
continue
|
||
if zeile.startswith("+++ b/"):
|
||
datei = zeile[6:]
|
||
continue
|
||
if not zeile.startswith("+") or zeile.startswith("+++"):
|
||
continue
|
||
for name, muster in GEHEIM.items():
|
||
for m in muster.finditer(zeile):
|
||
wert = m.groups()[-1] if m.groups() else m.group(0)
|
||
geheim[name].append((datei, len(wert)))
|
||
for name, muster in ZAEHLEN.items():
|
||
for m in muster.finditer(zeile):
|
||
zaehl[name][m.group(0)] += 1
|
||
|
||
print(f"Commits im Bereich: {commits}")
|
||
for name in GEHEIM:
|
||
treffer = geheim.get(name, [])
|
||
print(f"[geheim] {name}: {len(treffer)}")
|
||
for d, laenge in sorted(set(treffer))[:15]:
|
||
print(f" {d} (Wertlänge {laenge})")
|
||
for name, c in zaehl.items():
|
||
print(f"[zaehl] {name}: {sum(c.values())} Treffer, {len(c)} verschieden: " + ", ".join(f"{w} ×{n}" for w, n in c.most_common(12)))
|