$ kode

← alle Dateien · als Text

kode/freigabe.py

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99"""Was das Modell ohne Rueckfrage darf. Drei Modi (Umschalten mit Shift+Tab oder /modus): fragen - Lesen frei; jede Dateiaenderung und jeder Befehl wird erfragt dateien - Lesen und Aendern frei; Befehle werden erfragt alles - nichts wird erfragt Dazu dauerhafte Regeln je Projektordner ("immer erlauben"): fuer Befehle ein Praefix wie `git status` oder `npm run`, gespeichert in ~/.kode/freigaben.json. Ein zusammengesetzter Befehl (&&, ||, ;, |) ist nur frei, wenn jeder Teil frei ist - sonst waere `git status && rm -rf /` durch `git status` gedeckt. """ import json import re import shlex from pathlib import Path from . import konfig, werkzeuge MODI = ["fragen", "dateien", "alles"] MODUS_TEXT = {"fragen": "fragt bei Änderungen und Befehlen", "dateien": "Dateien frei, Befehle werden erfragt", "alles": "alles frei, keine Rückfragen"} def _laden() -> dict: try: return json.loads(konfig.FREIGABEN.read_text(encoding="utf-8")) except (OSError, ValueError): return {} def _speichern(d: dict) -> None: konfig.verzeichnisse() konfig.FREIGABEN.write_text(json.dumps(d, ensure_ascii=False, indent=1), encoding="utf-8") def regeln(projekt: Path) -> list[str]: return list(_laden().get(str(projekt), {}).get("bash", [])) def regel_hinzu(projekt: Path, praefix: str) -> None: d = _laden() eintrag = d.setdefault(str(projekt), {}).setdefault("bash", []) if praefix not in eintrag: eintrag.append(praefix) _speichern(d) def regel_weg(projekt: Path, praefix: str) -> bool: d = _laden() eintrag = d.get(str(projekt), {}).get("bash", []) if praefix in eintrag: eintrag.remove(praefix) _speichern(d) return True return False _TRENNER = re.compile(r"&&|\|\||;|\||\n") def teile(befehl: str) -> list[str]: return [t.strip() for t in _TRENNER.split(befehl) if t.strip()] def praefix_vorschlag(befehl: str) -> str: """Erstes Wort, bei Werkzeugen mit Unterbefehl die ersten zwei.""" try: w = shlex.split(teile(befehl)[0]) except (ValueError, IndexError): w = befehl.split() if not w: return befehl if w[0] in ("git", "npm", "pnpm", "yarn", "docker", "pip", "cargo", "go", "systemctl", "uv", "make", "kubectl", "python3", "python") and len(w) > 1: return " ".join(w[:2]) return w[0] def _gedeckt(teil: str, liste: list[str]) -> bool: # $( ) und Backticks fuehren beliebigen Code aus, > schreibt in beliebige # Dateien - nie per Regel frei. if "$(" in teil or "`" in teil or ">" in teil: return False return any(teil == r or teil.startswith(r + " ") for r in liste) def frei(modus: str, projekt: Path, name: str, args: dict) -> bool: if name in werkzeuge.NUR_LESEN or modus == "alles": return True if name in werkzeuge.DATEI_AENDERN: return modus == "dateien" if name == "bash": liste = regeln(projekt) t = teile(args.get("command") or "") return bool(t) and all(_gedeckt(x, liste) for x in t) return False