{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_mvc.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook MVC Regeln (BLOCK) - Controller-Reinheit.\n\nP2.x Regeln: MVC-Trennung, keine SQL\/Transaktionen\/Output in Controller\n\"\"\"\n\nimport re\nfrom typing import Optional\nfrom .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist, block\n\n\n# =============================================================================\n# PRÜFUNG 2: MVC + CRUD\n# =============================================================================\n\ndef p2_1_no_sql_in_controller(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.1: Keine SQL-Statements in Controller.\"\"\"\n if \"\/Controller\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n sql_patterns = [\n r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n r\"\\bINSERT\\s+INTO\\b\",\n r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n r\"\\bDELETE\\s+FROM\\b\",\n ]\n\n for pattern in sql_patterns:\n if re.search(pattern, content, re.IGNORECASE):\n return block(\"P2.1\", \"SQL statement in Controller. Move to Repository.\")\n\n return None\n\n\ndef p2_2_no_transactions_in_controller(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.2: Keine Transaktionen in Controller.\"\"\"\n if \"\/Controller\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n transaction_patterns = [\n r\"\\bbeginTransaction\\s*\\(\",\n r\"\\bcommit\\s*\\(\",\n r\"\\brollBack\\s*\\(\",\n r\"\\brollback\\s*\\(\",\n ]\n\n for pattern in transaction_patterns:\n if re.search(pattern, content):\n return block(\"P2.2\", \"Transaction control in Controller. Move to UseCase or Repository.\")\n\n return None\n\n\ndef p2_3_no_echo_in_controller(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.3: Kein echo\/print in Controller.\"\"\"\n if \"\/Controller\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n output_patterns = [\n r\"\\becho\\s+\",\n r\"\\becho\\(\",\n r\"\\bprint\\s+\",\n r\"\\bprint\\(\",\n ]\n\n for pattern in output_patterns:\n if re.search(pattern, content):\n return block(\"P2.3\", \"Direct output (echo\/print) in Controller. Use Response object.\")\n\n return None\n\n\ndef p2_4_no_db_in_usecases(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.4: Keine DB-Artefakte in UseCases.\"\"\"\n if \"\/UseCases\/\" not in file_path and \"\/Application\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n db_patterns = [\n r\"\\bnew\\s+PDO\\b\",\n r\"\\bPDO::\",\n r\"\\bDatabaseFactory::\",\n r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n r\"\\bINSERT\\s+INTO\\b\",\n r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n r\"\\bDELETE\\s+FROM\\b\",\n ]\n\n for pattern in db_patterns:\n if re.search(pattern, content, re.IGNORECASE):\n return block(\"P2.4\", \"DB artifact in UseCase\/Application. Use Repository interface.\")\n\n return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n p2_1_no_sql_in_controller,\n p2_2_no_transactions_in_controller,\n p2_3_no_echo_in_controller,\n p2_4_no_db_in_usecases,\n]\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_mvc.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook MVC Regeln (BLOCK) - Controller-Reinheit.\n\nP2.x Regeln: MVC-Trennung, keine SQL\/Transaktionen\/Output in Controller\n\"\"\"\n\nimport re\nfrom typing import Optional\nfrom .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist, block\n\n\n# =============================================================================\n# PRÜFUNG 2: MVC + CRUD\n# =============================================================================\n\ndef p2_1_no_sql_in_controller(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.1: Keine SQL-Statements in Controller.\"\"\"\n if \"\/Controller\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n sql_patterns = [\n r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n r\"\\bINSERT\\s+INTO\\b\",\n r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n r\"\\bDELETE\\s+FROM\\b\",\n ]\n\n for pattern in sql_patterns:\n if re.search(pattern, content, re.IGNORECASE):\n return block(\"P2.1\", \"SQL statement in Controller. Move to Repository.\")\n\n return None\n\n\ndef p2_2_no_transactions_in_controller(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.2: Keine Transaktionen in Controller.\"\"\"\n if \"\/Controller\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n transaction_patterns = [\n r\"\\bbeginTransaction\\s*\\(\",\n r\"\\bcommit\\s*\\(\",\n r\"\\brollBack\\s*\\(\",\n r\"\\brollback\\s*\\(\",\n ]\n\n for pattern in transaction_patterns:\n if re.search(pattern, content):\n return block(\"P2.2\", \"Transaction control in Controller. Move to UseCase or Repository.\")\n\n return None\n\n\ndef p2_3_no_echo_in_controller(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.3: Kein echo\/print in Controller.\"\"\"\n if \"\/Controller\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n output_patterns = [\n r\"\\becho\\s+\",\n r\"\\becho\\(\",\n r\"\\bprint\\s+\",\n r\"\\bprint\\(\",\n ]\n\n for pattern in output_patterns:\n if re.search(pattern, content):\n return block(\"P2.3\", \"Direct output (echo\/print) in Controller. Use Response object.\")\n\n return None\n\n\ndef p2_4_no_db_in_usecases(file_path: str, content: str) -> Optional[dict]:\n \"\"\"P2.4: Keine DB-Artefakte in UseCases.\"\"\"\n if \"\/UseCases\/\" not in file_path and \"\/Application\/\" not in file_path:\n return None\n if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n return None\n\n db_patterns = [\n r\"\\bnew\\s+PDO\\b\",\n r\"\\bPDO::\",\n r\"\\bDatabaseFactory::\",\n r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n r\"\\bINSERT\\s+INTO\\b\",\n r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n r\"\\bDELETE\\s+FROM\\b\",\n ]\n\n for pattern in db_patterns:\n if re.search(pattern, content, re.IGNORECASE):\n return block(\"P2.4\", \"DB artifact in UseCase\/Application. Use Repository interface.\")\n\n return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n p2_1_no_sql_in_controller,\n p2_2_no_transactions_in_controller,\n p2_3_no_echo_in_controller,\n p2_4_no_db_in_usecases,\n]\n",
"structuredPatch": [],
"originalFile": null
}
}