From fa22435d7c6ebf355296d056d01ecc088711f992 Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Thu, 16 Jul 2026 11:01:47 +0200 Subject: [PATCH] =?UTF-8?q?tabu-pfade-guard:=20V16=20=E2=80=94=20relative?= =?UTF-8?q?=20Pfade=20bei=20riskantem=20cwd=20blocken=20(Vorfall=2016.07.)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nacht-Worker t_7a05992a ersetzte 03:59 den Live-Checkout mit cwd=$HOME und RELATIVEN Pfaden (rm -rf mission-control-v2 && git clone ...) — der Guard prueft beim terminal-Tool nur den Kommandotext auf home-verankerte Formen und liess das durch (.venv weg -> Gateway 203/EXEC-Crash-Loop). Drei Stopfen: (1) cwd IN einem Tabu-Pfad -> jede Schreib-Operation geblockt (Lesen frei). (2) git clone/init mit Ziel direkt in $HOME geblockt (Worker klonen im Task-Workspace); explizites Ziel ausserhalb $HOME bleibt erlaubt. (3) cwd == Vorfahr eines Tabu-Pfads -> relative Nennung des Tabu-Ziels zaehlt als Treffer; Lookarounds lassen den Workspace-Klon-Pfad (.hermes/kanban/workspaces//mission-control-v2) weiter durch. Nebenbefund zu: clone fehlte in der git-Schreibliste (a) — auch git clone URL ~/mission-control-v2 war vorher ungeblockt. Lokal bewiesen: 35/35 Payload-Faelle (Vorfalls-Kommando geblockt, Workspace-Arbeit/Lesen frei, Alt-Verhalten regressionsfrei). Co-Authored-By: Claude Fable 5 --- deploy/agent-hooks/tabu-pfade-guard.py | 71 ++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/deploy/agent-hooks/tabu-pfade-guard.py b/deploy/agent-hooks/tabu-pfade-guard.py index 4a02445..ab7fcea 100644 --- a/deploy/agent-hooks/tabu-pfade-guard.py +++ b/deploy/agent-hooks/tabu-pfade-guard.py @@ -12,9 +12,11 @@ Task-Scope waere hier sogar gefaehrlich: Worker tragen als task_id den Session-Z (z.B. 20260713_224206_267304), NICHT die Kanban-t_-ID — ein "nur t_"-Check liesse also alle Worker-Schreibzugriffe durch (Bug 13.07., so entdeckt). -WICHTIG: Gesperrt wird NUR der home-verankerte Live-Checkout ($HOME/mission-control-v2), NIE -der blosse Name "mission-control-v2" (der Workspace-Klon enthaelt ihn) — sonst waere die -legitime Worker-Arbeit blockiert. Lesen ist erlaubt; geblockt werden nur Schreib-Operationen. +WICHTIG: Gesperrt wird der home-verankerte Live-Checkout ($HOME/mission-control-v2); der +blosse Name "mission-control-v2" zaehlt NUR, wenn er vom cwd aus in einen Tabu-Pfad aufloest +(V16 unten) — der Workspace-Klon (~/.hermes/kanban/workspaces//mission-control-v2) bleibt +frei, sonst waere die legitime Worker-Arbeit blockiert. Lesen ist erlaubt; geblockt werden +nur Schreib-Operationen. Ausgabe {"action":"block","message":...} = Tool geblockt; {} = durchlassen. """ @@ -78,6 +80,8 @@ if tool == "terminal": cmd = ti.get("command") or "" if not cmd: out({}) + cwd_raw = payload.get("cwd") or "" + cwd = os.path.realpath(os.path.expanduser(cwd_raw)) if cwd_raw else "" # R5 (Review 15.07.): `git init` im Kanban-Workspace hart blocken. Drei Naechte in # Folge bauten Worker git-init-Orphans ohne gemeinsamen Ursprung mit main # (cleanse-skill-index 12.07., skill-kanten-generator 13.07., blogwatcher-logging @@ -86,13 +90,50 @@ if tool == "terminal": # klonen (SOUL/GRENZEN). Scope: nur Kanban-Worker (Lucy/CLI duerfen z. B. fuer # projekt-start neue Repos initialisieren). ws_dir = os.path.join(home, ".hermes", "kanban", "workspaces") - if (payload.get("cwd") or "").startswith(ws_dir) and re.search(r"(?:^|[^\w])git\s+init\b", cmd): + if cwd_raw.startswith(ws_dir) and re.search(r"(?:^|[^\w])git\s+init\b", cmd): out({"action": "block", "message": ( "TABU (R5): `git init` ist im Worker-Workspace gesperrt — Orphan-Branches ohne " "gemeinsamen Ursprung mit main sind bei der Annahme technisch tot (Vorfaelle " "12.–14.07.). Richtiger Weg: VOLL klonen, z. B. git clone " "https://git.tobisniceshomelab.ddnsfree.com/Hitonabi/mission-control-v2 " "(Credentials liegen in ~/.git-credentials), dann Branch von origin/main abzweigen.")}) + # Schreib-Subkommandos von git (V16: + clone — `git clone URL ~/mission-control-v2` + # rutschte vorher an (a) UND (d) vorbei). + git_write = ( + r"(?:^|[^\w])git(?:\s+-C\s+\S+)?\s+(?:-[\w-]+\s+)*" + r"(commit|push|reset|checkout|switch|merge|add|rm|mv|clean|stash|rebase|apply|restore|init|clone|remote|config)\b" + ) + + # V16 (Vorfall 16.07.2026): Werkstatt-Worker t_7a05992a ersetzte um 03:59 den Live- + # Checkout mit cwd=$HOME und RELATIVEN Pfaden (`rm -rf mission-control-v2 && git + # clone ...`) — keine home-verankerte Tabu-Form im Kommandotext, der Guard sah nur + # Strings und liess durch (.venv weg -> Gateway im 203/EXEC-Crash-Loop). Drei Stopfen: + # (1) cwd liegt IN einem Tabu-Pfad -> jede Schreib-Operation blocken (relative Ziele + # treffen zwangslaeufig den Tabu-Baum). Lesen bleibt erlaubt. + if cwd and under_tabu(cwd): + if (re.search(git_write, cmd) or re.search(r"(?:^|[^\w])sed\s+-i", cmd) + or re.search(r"(?:^|[^\w])(rm|rmdir|mv|cp|truncate|dd|chmod|chown|ln|install|mkdir|touch)\s", cmd) + or re.search(r">>?\s*(?![/&])", cmd) + or re.search(r"(?:^|[^\w])tee\s+(?:-a\s+)?(?!/)", cmd)): + block() + # (2) cwd == $HOME -> `git clone`/`git init`, deren Ziel direkt in $HOME landet, + # blocken (exakt der Vorfalls-Hergang). Worker klonen im Task-Workspace; ein + # explizites Ziel AUSSERHALB von $HOME (z.B. Unterordner) bleibt erlaubt. + m = re.search(r"(?:^|[^\w])git(?:\s+-C\s+\S+)?\s+(?:-[\w-]+\s+)*(?:clone|init)\b([^|&;]*)", cmd) + if cwd == home and m: + toks = m.group(1).split() + dest = toks[-1] if toks else "" + # kein brauchbares Ziel-Argument (leer, Option oder URL) -> implizit direkt in $HOME + implicit = (not dest or dest.startswith("-") + or re.search(r"://|^git@|^[\w.-]+@[\w.-]+:|\.git$", dest)) + if (implicit or under_tabu(os.path.join(home, dest)) + or os.path.dirname(os.path.realpath(os.path.join(home, dest))) == home): + out({"action": "block", "message": ( + "TABU (V16): `git clone`/`git init` direkt in $HOME ist gesperrt — so wurde " + "am 16.07. der Live-Checkout ~/mission-control-v2 durch einen frischen Klon " + "ersetzt (.venv weg, Gateway tot). Klone/initialisiere in deinem Task-" + "Workspace (~/.hermes/kanban/workspaces//...) oder gib ein Ziel " + "ausserhalb von $HOME an.")}) # Home-verankerte Tabu-Formen. Der Workspace-Klon # ($HOME/.hermes/kanban/.../mission-control-v2) enthaelt KEINE davon als Teilstring. tabu_forms = [ @@ -100,16 +141,24 @@ if tool == "terminal": SRC, "~/.hermes/hermes-agent", "$HOME/.hermes/hermes-agent", "${HOME}/.hermes/hermes-agent", SYSD, "~/.config/systemd/user", "$HOME/.config/systemd/user", "${HOME}/.config/systemd/user", ] - tabu_alt = "(?:" + "|".join(re.escape(t) for t in tabu_forms) + ")" + # (3) cwd == Vorfahr eines Tabu-Pfads (z.B. $HOME) -> auch die RELATIVE Nennung des + # Tabu-Ziels (mission-control-v2, .hermes/hermes-agent, .config/systemd/user) + # zaehlt als Treffer fuer die Schreib-Checks unten. Die Lookarounds stellen + # sicher, dass der Name ein eigenes Argument beginnt — als Teil eines laengeren + # Pfades (z.B. .hermes/kanban/workspaces//mission-control-v2) matcht er + # NICHT, die legitime Workspace-Arbeit bleibt frei. + rel_pats = [] + if cwd: + for t in (LIVE, SRC, SYSD): + if t.startswith(cwd + os.sep): + rel = os.path.relpath(t, cwd).replace(os.sep, "/") + rel_pats.append(r"(? Workspace-Arbeit, durchlassen + out({}) # kein Tabu-Pfad referenziert -> Workspace-Arbeit, durchlassen # (a) git-Schreib-Subkommando (Repo-Kontext ist der referenzierte Tabu-Pfad). - if re.search( - r"(?:^|[^\w])git(?:\s+-C\s+\S+)?\s+(?:-[\w-]+\s+)*" - r"(commit|push|reset|checkout|switch|merge|add|rm|mv|clean|stash|rebase|apply|restore|init|remote|config)\b", - cmd, - ): + if re.search(git_write, cmd): block() # (b) sed -i, das einen Tabu-Pfad anfasst. if re.search(r"(?:^|[^\w])sed\s+-i\S*\s+[^|&;]*" + tabu_alt, cmd):