"""Hermes NL 輸入安全守門 — Python 重做 awoooi-guard.js(ADR-094) 從 .claude/hooks/awoooi-guard.js HARD BLOCK 規則移植, 涵蓋 K8s 生產刪除、docker volume 摧毀、force push、DROP TABLE 等。 2026-04-24 Claude Sonnet 4.6 (WS4 Hermes NL) """ from __future__ import annotations import re # ───────────────────────────────────────────────────────────────────────────── # DENY 正則:完整移植自 awoooi-guard.js HARD BLOCK 段落 # ───────────────────────────────────────────────────────────────────────────── _DENY_PATTERNS: list[re.Pattern] = [ # 基本破壞性指令 re.compile(r"rm\s+-rf", re.IGNORECASE), re.compile(r"git\s+push\s+.*--force", re.IGNORECASE), re.compile(r"DROP\s+TABLE", re.IGNORECASE), re.compile(r"truncate\s+table", re.IGNORECASE), re.compile(r"kubectl\s+delete\s+namespace", re.IGNORECASE), re.compile(r"systemctl\s+(stop|disable)\s+", re.IGNORECASE), re.compile(r"chmod\s+777", re.IGNORECASE), re.compile(r"curl.*(sh|bash)\s*\|", re.IGNORECASE), re.compile(r">\s*/etc/", re.IGNORECASE), # K8s 生產命名空間(移植自 awoooi-guard.js HARD BLOCK) re.compile(r"kubectl.*delete.*namespace.*awoooi-prod", re.IGNORECASE), # K8s 生產 PVC/Secret 強制刪除 re.compile(r"kubectl.*delete.*(pvc|secret).*-n.*awoooi-prod", re.IGNORECASE), re.compile(r"kubectl.*-n.*awoooi-prod.*delete.*(pvc|secret)", re.IGNORECASE), # docker compose down -v(摧毀 volume) re.compile(r"docker[\s-]?compose.*down.*(-v\b|--volumes)", re.IGNORECASE), # docker system prune -f re.compile(r"docker\s+system\s+prune.*(-f|--force)", re.IGNORECASE), # Telegram logOut / deleteWebhook(先停後換原則) re.compile(r"api\.telegram\.org/bot[^/]+/(logOut|deleteWebhook)", re.IGNORECASE), # psql DROP TABLE/DATABASE(非 test/dev 環境) re.compile(r"psql.*-c.*DROP\s+(TABLE|DATABASE|SCHEMA)", re.IGNORECASE), # force push 到 gitea main re.compile(r"git push.*(--force|-f).*gitea.*main", re.IGNORECASE), re.compile(r"git push.*gitea.*main.*(--force|-f)", re.IGNORECASE), # DROP DATABASE 直接語句 re.compile(r"DROP\s+DATABASE", re.IGNORECASE), # 清除 /dev/null 覆蓋系統檔案 re.compile(r">\s*/dev/(mem|kmem|port)", re.IGNORECASE), ] # ───────────────────────────────────────────────────────────────────────────── # MUTATE 正則:變更意圖偵測(需走審批流) # ───────────────────────────────────────────────────────────────────────────── # 只允許 query/describe/summarize 類動作(mutate/deploy/approve 走審批流) _MUTATE_PATTERNS: list[re.Pattern] = [ re.compile(r"\b(deploy|apply|create|delete|rollout|approve|execute)\b", re.IGNORECASE), ] def is_dangerous_input(text: str) -> bool: """True → 拒絕,回 ⛔""" return any(p.search(text) for p in _DENY_PATTERNS) def is_mutate_intent(text: str) -> bool: """True → 需要走 ApprovalRecord 二次確認""" return any(p.search(text) for p in _MUTATE_PATTERNS)