38 Datei(en) geändert. Erzeugt von .vault-tools/auto-push/werkzeuge_spiegel.py.
10 KiB
type, title, status, modell, summary, tags, created, updated
| type | title | status | modell | summary | tags | created | updated | ||
|---|---|---|---|---|---|---|---|---|---|
| plan | Task 01: shell_wache.py und Regressionstreiber | active | sonnet | PreToolUse-Hook, der im Befehl des Bash-Werkzeugs vier Windows-Shell-Fallen erkennt und nur warnt, dazu ein Treiber mit Treffer- und Gegenfall je Muster. |
|
2026-10-30 | 2026-10-30 |
Übersicht: 00-uebersicht.md
Task 01: shell_wache.py und Regressionstreiber
Ziel
Zwei neue Dateien, sonst nichts:
D:/Claude/.vault-tools/shell-wache/shell_wache.pyD:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py
Verboten: .claude/settings.json anfassen, irgendeine andere Datei ändern, committen. Nur Standardbibliothek, keine Importe aus dem eigenen Ordner (der Hook läuft mit python -I, das den Skriptordner aus sys.path nimmt). Python immer als python -I -X utf8, nie python3. Kein Heredoc für Python-Code: Dateien mit dem Write-Werkzeug anlegen. Vor jedem Lauf python -m py_compile <datei>.
Die verbindliche Spezifikation ist die Entscheidungsliste in D:/Claude/.vault-tools/shell-wache/ENTWURF.md (Fragen 1 bis 20) samt Angriffsliste. Lies sie ganz. Was hier steht, ergänzt sie; bei Widerspruch gilt ENTWURF.md, und du meldest den Widerspruch.
shell_wache.py
Kopfkommentar (Prüfskript, also volle Länge nach AI/Regelwerk/codedoku.md, „Ausnahme: Prüfskripte“), im Stil von D:/Claude/.vault-tools/session-hooks/regelwerk_nach_edit.py:
"""PreToolUse-Hook auf das Bash-Werkzeug: warnt vor vier bekannten Windows-Shell-Fallen, lehnt nie ab.
Ersteller: Pascal Michel
Erstellt: 2026-10-30
Entwurf: ENTWURF.md im selben Ordner · Doku: docs/code/shell_wache.md
GELTUNGSBEREICH
(vier Muster, Windows-Kontext, Linux-Weiche, Ausgabeform, immer Exit 0 – knapp aus ENTWURF.md)
BENANNTE LÜCKEN
(die Liste aus ENTWURF.md, Abschnitt „Benannte Lücken“)
DIESER HOOK IST EIN FANGNETZ, KEIN NACHWEIS. ...
"""
Ablauf:
- stdin als Bytes lesen,
decode("utf-8", "replace"). - Eingabe prüfen (ENTWURF 14 bis 16). Gründe für
[eingabe], wörtlich:stdin leer,kein JSON,kein JSON-Objekt,tool_input fehlt oder ist kein Objekt,command fehlt oder ist keine Zeichenkette,tool_name ist <name>, nicht Bash. Leerercommand(""oder nur Leerraum): still. - Die vier Detektoren laufen je in eigenem
try; ein Fehler darin wird[intern]mittype(e).__name__, die übrigen laufen weiter. - Ausgabe: ohne Befund nichts auf stdout. Mit Befund
json.dumps({"hookSpecificOutput": {"hookEventName": "PreToolUse", "additionalContext": text}}, ensure_ascii=True)auf stdout. NiepermissionDecision, niedecision, niecontinue. main()intry/except BaseException(außerKeyboardInterruptist egal); auch ein Fehler dort wird als[intern]ausgegeben. Das Skript endet in jedem Fall mit Exit 0 – nach der Ausgabesys.exit(0)am Ende vonif __name__ == "__main__":.
Text des Hinweises: erste Zeile wörtlich shell-wache warnt nur, der Befehl läuft trotzdem. Danach je getroffenem Schlüssel genau eine Zeile in dieser Reihenfolge, auch wenn das Muster mehrfach trifft:
| Schlüssel | Zeile (wörtlich) |
|---|---|
[python-I] |
[python-I] python -I ohne -X utf8: -I ignoriert PYTHONIOENCODING, Umlaute brechen unter cp1252. Nimm python -I -X utf8 (AI/Regelwerk/windows-shell.md, „Welches Python“). |
[python3] |
[python3] python3 ist unter Windows der Microsoft-Store-Stub. Nimm python (AI/Regelwerk/windows-shell.md, „Welches Python“). |
[bash-windows] |
[bash-windows] bash ohne Pfad aus cmd, PowerShell oder Python startet unter Windows den WSL-Starter. Git-Bash mit vollem Pfad C:\Program Files\Git\bin\bash.exe, WSL mit wsl -d <Distro> (AI/Regelwerk/windows-shell.md, „Welches Python“). |
[pkill-f] |
[pkill-f] pkill -f trifft jede Befehlszeile mit dem Muster, auch die eigene Shell und fremde Läufe in WSL. Erst pgrep -af lesen, dann gezielt kill <pid> oder ein Muster wie "[s]leep 7199" (Erinnerung pkill-f-trifft-die-eigene-shell; AI/Regelwerk/windows-shell.md). |
[eingabe] |
[eingabe] Hook-Eingabe unbrauchbar (<grund>), nichts geprüft. |
[intern] |
[intern] shell-wache ist abgestürzt (<Ausnahmetyp>), Prüfung unvollständig. |
Detektoren
Regeln genau nach ENTWURF.md, Fragen 8 bis 13 und 19. Ergänzend, damit es eindeutig ist:
- Wortgrenze: Ein Programmwort beginnt am Befehlsanfang oder nach Leerraum,
;,&,|,(,`,',",=; es darf einen Pfad davor tragen (/c/x/python.exe,"C:/P/python.exe"), außer beibash(Frage 9). Es endet vor Leerraum,',",;,),|,&oder dem Textende.python3-config,.bashrc,bash_profile,bash-completion,ipython,mypythonsind keine Programmwörter. python3:python3undpython3.<n>, mit oder ohne.exe.- Optionen lesen (
python,pkill): den Rest der Zeile nach dem Programmwort an Leerraum zerlegen, je Wort umschließende'und"abstreifen, Wörter lesen, solange sie mit-beginnen und nicht genau-oder--sind. - Windows-Kontext (a): gilt vom Kontextwort bis zum Zeilenende. Darin trifft jedes
bashoderbash.exeohne Pfad, vor dem auf derselben Zeile hinter dem Kontextwort keine Weiche steht. - Muster (b): regulärer Ausdruck auf
[plus optionalem Leerraum plus"bash",'bash',"bash.exe"oder'bash.exe', ohne Rücksicht auf Groß- und Kleinschreibung.
faelle_shell_wache.py
Treiber im Stil von D:/Claude/.vault-tools/session-hooks/faelle_nach_edit.py: startet den Hook je Fall als eigenen Prozess ([sys.executable, "-I", "-X", "utf8", HOOK], stdin als Bytes, Zeitlimit 10 s), ohne PYTHONIOENCODING in der Umgebung. Je Fall prüft er:
- Exit-Code ist 0 (nie 2),
- stdout ist leer, wenn der Fall still sein soll; sonst genau ein JSON-Objekt,
hookSpecificOutput.hookEventName == "PreToolUse", kein SchlüsselpermissionDecisionirgendwo, und die Menge der Schlüssel in eckigen Klammern am Zeilenanfang vonadditionalContextist genau die erwartete, - die Laufzeit liegt unter 2 s.
Grundlinie zuerst: Ein gesunder Befehl (ls -la && python -I -X utf8 x.py) muss still sein. Scheitert sie, bricht der Treiber ab, weil der Rest nichts misst.
Fälle, jeder als Eintrag (name, stdin_bytes, erwartete_schluessel). Ein Hilfsaufbau eingabe(cmd) baut {"tool_name": "Bash", "tool_input": {"command": cmd}}. Pflicht, mindestens:
| Gruppe | Treffer | Gegenfall (still) |
|---|---|---|
| python-I | python -I x.py; python -IB x.py; python -BI x.py; python -Ic "print(1)"; python -I -c "x" -X utf8; python -I -X utf8=0 x.py; /c/Py/python.exe -I x.py; "C:/Py/python.exe" -I x.py; cd a && python -I x.py; PYTHONIOENCODING=utf-8 python -I x.py |
python -I -X utf8 x.py; python -IXutf8 x.py; python -IX utf8 x.py; python -X utf8 -I -B x.py; python -I -X utf8=1 x.py; python x.py -I; python -c "import x" -I; ipython -I; python -m pytest -I |
| python3 | python3 x.py; python3.12 -m venv v; ls; python3 -V; python3.exe x.py |
python x.py; python3-config --libs; wsl -d Debian -- python3 x.py; ssh h python3 x.py; docker exec c python3 x.py |
| bash-windows | cmd //c "bash x.sh"; cmd.exe /c bash -c "x"; CMD /C BASH x; powershell -c "bash -lc 'x'"; powershell.exe -NoProfile -Command "& bash x"; pwsh -c "bash x"; python -X utf8 -c "import subprocess; subprocess.run(['bash', '-c', 'x'])"; python x.py <<'EOF' + Zeilenumbruch + subprocess.run(["bash.exe", "x"]) + Zeilenumbruch + EOF |
bash -c "x"; bash x.sh; cmd //c "C:/Program Files/Git/bin/bash.exe" x; cmd //c "C:\Program Files\Git\bin\bash.exe" x; powershell -c "wsl -d Debian -- bash -lc 'x'"; cmd /c "ssh.exe -o A=B root@h bash -s < f.sh"; powershell -c "Get-Content ~/.bashrc"; powershell -c "x" + Zeilenumbruch + bash y.sh; ssh h python - <<'EOF' + Zeilenumbruch + subprocess.run(['bash','x']) + Zeilenumbruch + EOF; subprocess.run([GITBASH, '-c']) in python -c |
| pkill-f | pkill -f x; pkill -TERM -f "[s]leep 1"; pkill -fx x; pkill --full x; pkill -u root -f x; sudo pkill -f x; wsl -d Debian -- bash -c 'a; pkill -f "y"'; /usr/bin/pkill -f x |
pkill x; pkill -TERM x; pgrep -f x; pkill -u fx x (fx ist Argument von -u) |
| mehrere | alle vier Muster in einem Befehl: vier Schlüssel; python -I a.py; python -I b.py: ein Schlüssel |
|
| Reihenfolge | je Muster: Treffer am Anfang (<treffer>; ls), am Ende (ls; <treffer>), in Zeile 3 eines dreizeiligen Befehls |
|
| Eingabe | leer; nicht json; []; null; {}; {"tool_name": "Bash"}; tool_input null, als Zeichenkette "x"; command fehlt, null, 5, ["ls"]; ungültiges UTF-8 (b'{"tool_name":"Bash","tool_input":{"command":"ls \xff"}}', still); tool_name PowerShell mit python3 x → nur [eingabe] |
command gleich "" und " ": still; zusätzliche Felder ("tool_use_id", "description", "timeout"): still |
Ausgabe des Treibers: je Fall eine Zeile OK oder FEHLER mit Name und Abweichung, am Ende die Zahl der Fälle und der Fehler, gerechnet, nicht geschrieben. Exit 0 nur, wenn alle Fälle passen; Exit 1 sonst; ein Absturz des Treibers selbst wird gefangen und als Fehler gezählt.
Prüfen
# Syntax beider Dateien
python -I -X utf8 -m py_compile D:/Claude/.vault-tools/shell-wache/shell_wache.py D:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py
# Alle Fälle
python -I -X utf8 D:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py
# Kein CR in den neuen Dateien (je 0)
tr -cd '\r' < D:/Claude/.vault-tools/shell-wache/shell_wache.py | wc -c
tr -cd '\r' < D:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py | wc -c
Rotnachweis, Pflicht im Bericht: Kopiere shell_wache.py in einen neuen Ordner unter dem Scratchpad oder mktemp -d, lösche dort je Detektor dessen Aufruf (vier Mutanten, einzeln, mit Rücksetzen) und fahre den Treiber gegen die Kopie (der Treiber nimmt den Hook-Pfad als optionales erstes Argument). Jeder Mutant muss mindestens einen FEHLER erzeugen. Nenne je Mutant die Zahl der Fehler.
Berichte: Zahl der Fälle, Ergebnis, die vier Mutanten mit Fehlerzahl, jede Stelle, an der du von diesem Task oder ENTWURF.md abgewichen bist, und jeden Fall, den du nicht eindeutig umsetzen konntest.