Files
Pascal MichelandClaude Opus 5.5 8950af05e0 Mutantenlauf auf der Kopie: lo im Netz-Namensraum, fail-closed, Treiber W1 bis W13
mutanten_lauf_kopie.sh faehrt lo im eigenen Netz-Namensraum hoch (unshare --keep-caps, ip, setpriv,
exec in python3) und prueft das vorher fail-closed (ip, setpriv, python3, lo-Probe mit
Faehigkeitspruefung). Anlass: Im Paket RB3 der Trading Plattform scheiterten Tests mit Server auf
127.0.0.1 im Namensraum an Errno 101 (lo aus).
Neuer Treiber mutanten_lauf_kopie_treiber.py mit W1 bis W13; Regressionsnachweis gegen HEAD (W1, W2,
W4, W5, W9 rot) und neun kaputte Varianten v1 bis v9, je an ihren Faellen rot. Lauf mit echten Daten:
alle 73 RB3-Mutanten durch das neue Skript 73/73 OK, T1 und T12 jetzt im Namensraum. Ein pruefer
las Skript und Treiber (kein blockierender Befund; der wichtige zu fehlenden Faellen fuer die
Fehlerzweige ist mit Nachbesserung 1 behoben, W10 bis W13).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JU1LsUp4FdmPq8mvC368G3
2026-10-09 15:20:49 +02:00

159 lines
7.8 KiB
Bash

#!/usr/bin/env bash
# Mutantenlauf auf einer Kopie der Kopie, gedacht fuer den Hilfsagenten der Plankritik
# (.claude/workflows/plankritik.js). Nie in der uebergebenen Kopie selbst arbeiten: Die Linsen
# fahren dort gleichzeitig Tests, und mutanten_lauf.py setzt Dateien zwischenzeitlich zurueck.
#
# Aufruf: bash mutanten_lauf_kopie.sh <kopie als Windows-Pfad> <planordner als Windows-Pfad> <modus>
# modus ist "voll" oder "muster". Der ganze Lauf ist auf BUDGET_S Sekunden begrenzt (Vorgabe 540),
# weil das Bash-Werkzeug eines Hilfsagenten nach hoechstens zehn Minuten endet; eine ueberschrittene
# Grenze meldet die Zeile ZEITGRENZE, die bis dahin gefahrenen Mutanten stehen in der Ausgabe.
# Beide Pfade sind Windows-Pfade (auch Kurznamen wie PASCAL~1),
# die dieses Skript selbst mit wslpath in WSL-Pfade umwandelt.
#
# Nachbesserung 2026-09-28 (Pruefbericht vom selben Tag, Befunde 1 bis 4, 7):
# - Netzsperre: mutanten_lauf.py laeuft in einem eigenen Netz-Namensraum
# (unshare --map-current-user --net). Gemessen in WSL Debian als Benutzer claude
# (util-linux 2.41.5): UID innen bleibt 1000 wie aussen, nur Schnittstelle lo, jeder
# Verbindungsversuch nach aussen (etwa 127.0.0.1:8144, wo das Dashboard lauscht) scheitert.
# Fehlt unshare ohne Netz, bricht das Skript vorher ab statt einen ungesperrten Mutantenlauf
# still durchzulassen.
# - Ungepufferte Ausgabe: python3 -u, damit bei einer Zeitgrenze die bis dahin gefahrenen
# Mutanten wirklich in der Ausgabe stehen.
# - Zeitlimit je Mutant: ZEITLIMIT_S (Vorgabe 120 s, kleiner als BUDGET_S) geht als --zeitlimit
# an mutanten_lauf.py; B1 mit 74 Mutanten brauchte 168 s gesamt, rund 2 s je Mutant.
# - Modus-Zeile vor dem Lauf ("modus: voll" / "modus: muster (...)"), damit die Linse weiss, ob
# ueberhaupt ein Mutant gefahren ist.
# - Aufraeumen bei SIGTERM: timeout -k 15 schickt nach der Frist noch ein SIGKILL hinterher, falls
# mutanten_lauf.py nach dem SIGTERM nicht rechtzeitig endet; sein eigener SIGTERM-Handler setzt
# die Zieldatei vorher zurueck und raeumt seine Temp-Ordner auf.
# - chmod 700 auf die Kopie der Kopie direkt nach dem Kopieren.
#
# Nachbesserung 2026-10-09 (Paket RB3 der Trading Plattform, Plan
# docs/plans/2026-10-09-mutanten-kopie-loopback.md):
# - lo im Netz-Namensraum: Im frischen Namensraum war lo aus; jeder Test mit einem Server auf
# 127.0.0.1 scheiterte mit Errno 101, auch ohne Mutant. Jetzt startet unshare zusaetzlich mit
# --keep-caps, eine sh faehrt lo mit ip hoch, und setpriv wirft danach alle vererbbaren und
# ambienten Faehigkeiten ab und geht per exec in python3 ueber. Gemessen vom Koordinator am
# 2026-10-09 in WSL Debian als Benutzer claude: uid 1000 wie aussen, CapEff und CapAmb 0,
# Verbindung ueber 127.0.0.1 gelingt. 127.0.0.1 ist das lo des Namensraums, nicht das des
# Rechners; ein Dienst des Rechners auf 127.0.0.1 bleibt unerreichbar (Treiber-Fall W8).
# - exec-Kette: unshare, sh, setpriv und python3 sind derselbe Prozess, das SIGTERM von timeout
# trifft den Laeufer selbst; keine Zwischenschale bleibt stehen (Treiber-Fall W6).
# - Vorpruefung vor dem Kopieren: Fehlt ip, setpriv oder python3, kommt lo in einer Probe im
# eigenen Namensraum nicht hoch, gelingt dort keine Verbindung ueber 127.0.0.1 oder hat die
# Probe nach setpriv noch CapEff oder CapAmb ungleich 0, endet das Skript mit
# MUTANTEN_LAUF_FEHLER und Exit 2, ohne EXIT-Zeile. Die Faehigkeiten prueft das Skript nur an
# dieser Probe und nur CapEff und CapAmb, nicht am Laeufer selbst; alle vier Mengen am Laeufer
# prueft Treiber-Fall W3. Die Vorpruefung steht vor der Suche nach mutanten.toml: Scheitert
# sie, endet das Skript auch fuer einen Planordner ohne mutanten.toml mit Exit 2 statt mit
# KEINE_MUTANTEN_TOML.
# - Hauptaufruf: Kommt lo dort nicht hoch, endet das Skript nach der Modus-Zeile mit
# MUTANTEN_LAUF_FEHLER, Exit 2 und EXIT-Zeile, ohne den Laeufer zu starten. Ein Lauf ohne
# Netzsperre oder ohne lo ist nie die Ausweichloesung.
# - Treiber: mutanten_lauf_kopie_treiber.py, Faelle W1 bis W13 (W10 bis W13 seit Nachbesserung 1,
# docs/plans/2026-10-09-mutanten-kopie-loopback-nachbesserung-1.md).
set -u
KOPIE_WIN="${1:-}"
PLANORDNER_WIN="${2:-}"
MODUS="${3:-}"
if [ -z "$KOPIE_WIN" ] || [ -z "$PLANORDNER_WIN" ] || [ -z "$MODUS" ]; then
echo "MUTANTEN_LAUF_FEHLER: Aufruf braucht <kopie> <planordner> <modus>"
exit 2
fi
if [ "$MODUS" != "voll" ] && [ "$MODUS" != "muster" ]; then
echo "MUTANTEN_LAUF_FEHLER: modus muss voll oder muster sein, nicht $MODUS"
exit 2
fi
BUDGET_S="${BUDGET_S:-540}"
ZEITLIMIT_S="${ZEITLIMIT_S:-120}"
if ! [ "$ZEITLIMIT_S" -lt "$BUDGET_S" ] 2>/dev/null; then
echo "MUTANTEN_LAUF_FEHLER: ZEITLIMIT_S ($ZEITLIMIT_S) muss kleiner sein als BUDGET_S ($BUDGET_S)"
exit 2
fi
if ! unshare --map-current-user --net true 2>/dev/null; then
echo "MUTANTEN_LAUF_FEHLER: unshare ohne Netz nicht moeglich"
exit 2
fi
# Vorpruefung lo (seit 2026-10-09): Werkzeuge da, lo kommt im eigenen Namensraum hoch, eine
# Verbindung ueber 127.0.0.1 gelingt, und nach setpriv sind CapEff und CapAmb 0. Scheitert
# etwas davon, endet das Skript hier, vor dem Kopieren.
for WERKZEUG in ip setpriv python3; do
if ! command -v "$WERKZEUG" >/dev/null 2>&1; then
echo "MUTANTEN_LAUF_FEHLER: $WERKZEUG fehlt (gebraucht fuer lo im Netz-Namensraum)"
exit 2
fi
done
LO_PROBE='import socket
server = socket.socket()
server.bind(("127.0.0.1", 0))
server.listen(1)
client = socket.create_connection(server.getsockname(), timeout=3)
client.close()
server.close()
felder = {}
for zeile in open("/proc/self/status", encoding="utf-8", errors="replace"):
schluessel, _, wert = zeile.partition(":")
felder[schluessel] = wert.strip()
if int(felder["CapEff"], 16) != 0 or int(felder["CapAmb"], 16) != 0:
raise SystemExit("Faehigkeiten nicht abgeworfen: CapEff " + felder["CapEff"] + ", CapAmb " + felder["CapAmb"])
print("lo-probe ok")'
LO_AUSGABE=$(timeout 30 unshare --map-current-user --net --keep-caps \
sh -c 'ip link set lo up && exec setpriv --inh-caps=-all --ambient-caps=-all -- python3 -c "$1"' sh "$LO_PROBE" 2>&1)
LO_EXIT=$?
if [ "$LO_EXIT" -ne 0 ] || [ "$LO_AUSGABE" != "lo-probe ok" ]; then
echo "MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht nutzbar (Exit $LO_EXIT): $LO_AUSGABE"
exit 2
fi
KOPIE=$(wslpath -u "$KOPIE_WIN")
PLANORDNER=$(wslpath -u "$PLANORDNER_WIN")
if [ ! -d "$KOPIE" ]; then
echo "MUTANTEN_LAUF_FEHLER: Kopie fehlt: $KOPIE"
exit 2
fi
if [ ! -d "$PLANORDNER" ]; then
echo "MUTANTEN_LAUF_FEHLER: Planordner fehlt: $PLANORDNER"
exit 2
fi
MUTANTEN_TOML="$PLANORDNER/mutanten.toml"
if [ ! -f "$MUTANTEN_TOML" ]; then
echo "KEINE_MUTANTEN_TOML"
exit 0
fi
TMPKOPIE=$(mktemp -d /tmp/plankritik-mut-XXXXXX)
trap 'rm -rf "$TMPKOPIE"' EXIT
cp -a "$KOPIE/." "$TMPKOPIE/"
chmod 700 "$TMPKOPIE"
find "$TMPKOPIE" -name __pycache__ -type d -prune -exec rm -rf {} +
MUSTER_SCHALTER=""
if [ "$MODUS" = "muster" ]; then
MUSTER_SCHALTER="--nur-muster"
echo "modus: muster (nur Musterpruefung, kein Mutant gefahren)"
else
echo "modus: voll"
fi
# exec-Kette ohne Zwischenschale (seit 2026-10-09): unshare, sh, setpriv und python3 sind derselbe
# Prozess, das SIGTERM von timeout trifft den Laeufer selbst (Treiber-Fall W6).
timeout -k 15 "$BUDGET_S" unshare --map-current-user --net --keep-caps \
sh -c 'ip link set lo up || { echo "MUTANTEN_LAUF_FEHLER: lo im Netz-Namensraum nicht hochgefahren"; exit 2; }; exec setpriv --inh-caps=-all --ambient-caps=-all -- "$@"' sh \
python3 -u /mnt/d/Claude/.vault-tools/plan-kopie/mutanten_lauf.py \
--wurzel "$TMPKOPIE" --mutanten "$MUTANTEN_TOML" --zeitlimit "$ZEITLIMIT_S" $MUSTER_SCHALTER
EXITCODE=$?
if [ "$EXITCODE" -eq 124 ]; then
echo "ZEITGRENZE: Mutantenlauf nach $BUDGET_S s abgebrochen; Mutanten ohne Zeile oben sind nicht gefahren"
fi
echo "EXIT mutanten_lauf: $EXITCODE"
exit $EXITCODE