{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_failfast.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nPost-Hook Fail Fast Regeln (WARN) - Fail Fast Principle.\n\nW8.x Regeln: Warnt bei stillen Fehlern, Error Suppression und leeren Catch-Blöcken.\n\nPrinzip: \"Ungültige Zustände werden sofort erkannt. Fehler werden früh sichtbar gemacht.\"\n\"\"\"\n\nimport re\nfrom typing import List\nfrom .rule_base import Rule, GLOBAL_ALLOWLIST\n\n\n# =============================================================================\n# W8: FAIL FAST\n# =============================================================================\n\nclass W8_1_EmptyCatch(Rule):\n \"\"\"W8.1: Leerer catch-Block - Fehler werden verschluckt.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Leerer catch-Block: catch(...) { } oder catch(...) { \/\/ comment }\n # Pattern: catch mit leerem Body oder nur Whitespace\/Kommentaren\n empty_catch_patterns = [\n # Komplett leer\n r\"catch\\s*\\([^)]+\\)\\s*\\{\\s*\\}\",\n # Nur Whitespace\n r\"catch\\s*\\([^)]+\\)\\s*\\{\\s+\\}\",\n ]\n\n for pattern in empty_catch_patterns:\n matches = re.findall(pattern, content)\n if matches:\n warnings.append(\n f\"W8.1: Empty catch block detected. Errors are silently swallowed. \"\n f\"Log the exception or re-throw.\"\n )\n break\n\n return warnings\n\n\nclass W8_2_ErrorSuppression(Rule):\n \"\"\"W8.2: Error Suppression mit @ Operator.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # @ vor Variablen: @$var\n var_suppression = re.findall(r\"@\\s*\\$\\w+\", content)\n\n # @ vor Funktionen: @function()\n func_suppression = re.findall(r\"@\\s*[a-zA-Z_]\\w*\\s*\\(\", content)\n\n total = len(var_suppression) + len(func_suppression)\n\n if total > 0:\n warnings.append(\n f\"W8.2: Error suppression with @ operator ({total}x). \"\n f\"Handle errors explicitly instead of suppressing.\"\n )\n\n return warnings\n\n\nclass W8_3_CatchWithOnlyReturn(Rule):\n \"\"\"W8.3: Catch-Block der nur return enthält - versteckt Fehlerursache.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # catch(...) { return null; } oder { return false; } oder { return; }\n pattern = r\"catch\\s*\\([^)]+\\)\\s*\\{\\s*return\\s*(?:null|false|true|\\d+)?;\\s*\\}\"\n\n matches = re.findall(pattern, content, re.IGNORECASE)\n if matches:\n warnings.append(\n f\"W8.3: Catch block only returns without logging ({len(matches)}x). \"\n f\"Consider logging the exception before returning.\"\n )\n\n return warnings\n\n\nclass W8_4_GenericException(Rule):\n \"\"\"W8.4: Generische Exception ohne spezifischen Catch.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # catch (Exception $e) oder catch (\\Exception $e) ohne spezifischere catches davor\n generic_catch = re.findall(r\"catch\\s*\\(\\s*\\\\?Exception\\s+\\$\\w+\\s*\\)\", content)\n\n # Wenn generischer Catch, prüfe ob spezifische Catches vorhanden sind\n if generic_catch:\n specific_catch = re.findall(\n r\"catch\\s*\\(\\s*\\\\?(?!Exception\\b)[A-Z]\\w*Exception\\s+\\$\\w+\\s*\\)\",\n content\n )\n\n if not specific_catch:\n warnings.append(\n f\"W8.4: Only generic Exception catch without specific handlers. \"\n f\"Consider catching specific exceptions first.\"\n )\n\n return warnings\n\n\nclass W8_5_ThrowInDestructor(Rule):\n \"\"\"W8.5: Exception in Destructor - kann zu unerwartetem Verhalten führen.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Finde __destruct Methoden\n destruct_match = re.search(\n r\"function\\s+__destruct\\s*\\([^)]*\\)\\s*(?::\\s*\\w+)?\\s*\\{\",\n content\n )\n\n if destruct_match:\n # Suche throw innerhalb des Destructors (vereinfacht)\n destruct_start = destruct_match.end()\n # Finde das Ende des Destructors\n brace_count = 1\n pos = destruct_start\n while pos < len(content) and brace_count > 0:\n if content[pos] == '{':\n brace_count += 1\n elif content[pos] == '}':\n brace_count -= 1\n pos += 1\n\n destruct_body = content[destruct_start:pos]\n\n if re.search(r\"\\bthrow\\s+\", destruct_body):\n warnings.append(\n \"W8.5: Exception thrown in __destruct(). \"\n \"This can cause unexpected behavior. Catch and log instead.\"\n )\n\n return warnings\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n W8_1_EmptyCatch(),\n W8_2_ErrorSuppression(),\n W8_3_CatchWithOnlyReturn(),\n W8_4_GenericException(),\n W8_5_ThrowInDestructor(),\n]\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_failfast.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nPost-Hook Fail Fast Regeln (WARN) - Fail Fast Principle.\n\nW8.x Regeln: Warnt bei stillen Fehlern, Error Suppression und leeren Catch-Blöcken.\n\nPrinzip: \"Ungültige Zustände werden sofort erkannt. Fehler werden früh sichtbar gemacht.\"\n\"\"\"\n\nimport re\nfrom typing import List\nfrom .rule_base import Rule, GLOBAL_ALLOWLIST\n\n\n# =============================================================================\n# W8: FAIL FAST\n# =============================================================================\n\nclass W8_1_EmptyCatch(Rule):\n \"\"\"W8.1: Leerer catch-Block - Fehler werden verschluckt.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Leerer catch-Block: catch(...) { } oder catch(...) { \/\/ comment }\n # Pattern: catch mit leerem Body oder nur Whitespace\/Kommentaren\n empty_catch_patterns = [\n # Komplett leer\n r\"catch\\s*\\([^)]+\\)\\s*\\{\\s*\\}\",\n # Nur Whitespace\n r\"catch\\s*\\([^)]+\\)\\s*\\{\\s+\\}\",\n ]\n\n for pattern in empty_catch_patterns:\n matches = re.findall(pattern, content)\n if matches:\n warnings.append(\n f\"W8.1: Empty catch block detected. Errors are silently swallowed. \"\n f\"Log the exception or re-throw.\"\n )\n break\n\n return warnings\n\n\nclass W8_2_ErrorSuppression(Rule):\n \"\"\"W8.2: Error Suppression mit @ Operator.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # @ vor Variablen: @$var\n var_suppression = re.findall(r\"@\\s*\\$\\w+\", content)\n\n # @ vor Funktionen: @function()\n func_suppression = re.findall(r\"@\\s*[a-zA-Z_]\\w*\\s*\\(\", content)\n\n total = len(var_suppression) + len(func_suppression)\n\n if total > 0:\n warnings.append(\n f\"W8.2: Error suppression with @ operator ({total}x). \"\n f\"Handle errors explicitly instead of suppressing.\"\n )\n\n return warnings\n\n\nclass W8_3_CatchWithOnlyReturn(Rule):\n \"\"\"W8.3: Catch-Block der nur return enthält - versteckt Fehlerursache.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # catch(...) { return null; } oder { return false; } oder { return; }\n pattern = r\"catch\\s*\\([^)]+\\)\\s*\\{\\s*return\\s*(?:null|false|true|\\d+)?;\\s*\\}\"\n\n matches = re.findall(pattern, content, re.IGNORECASE)\n if matches:\n warnings.append(\n f\"W8.3: Catch block only returns without logging ({len(matches)}x). \"\n f\"Consider logging the exception before returning.\"\n )\n\n return warnings\n\n\nclass W8_4_GenericException(Rule):\n \"\"\"W8.4: Generische Exception ohne spezifischen Catch.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # catch (Exception $e) oder catch (\\Exception $e) ohne spezifischere catches davor\n generic_catch = re.findall(r\"catch\\s*\\(\\s*\\\\?Exception\\s+\\$\\w+\\s*\\)\", content)\n\n # Wenn generischer Catch, prüfe ob spezifische Catches vorhanden sind\n if generic_catch:\n specific_catch = re.findall(\n r\"catch\\s*\\(\\s*\\\\?(?!Exception\\b)[A-Z]\\w*Exception\\s+\\$\\w+\\s*\\)\",\n content\n )\n\n if not specific_catch:\n warnings.append(\n f\"W8.4: Only generic Exception catch without specific handlers. \"\n f\"Consider catching specific exceptions first.\"\n )\n\n return warnings\n\n\nclass W8_5_ThrowInDestructor(Rule):\n \"\"\"W8.5: Exception in Destructor - kann zu unerwartetem Verhalten führen.\"\"\"\n\n def check(self, file_path: str, content: str) -> List[str]:\n warnings = []\n\n # Finde __destruct Methoden\n destruct_match = re.search(\n r\"function\\s+__destruct\\s*\\([^)]*\\)\\s*(?::\\s*\\w+)?\\s*\\{\",\n content\n )\n\n if destruct_match:\n # Suche throw innerhalb des Destructors (vereinfacht)\n destruct_start = destruct_match.end()\n # Finde das Ende des Destructors\n brace_count = 1\n pos = destruct_start\n while pos < len(content) and brace_count > 0:\n if content[pos] == '{':\n brace_count += 1\n elif content[pos] == '}':\n brace_count -= 1\n pos += 1\n\n destruct_body = content[destruct_start:pos]\n\n if re.search(r\"\\bthrow\\s+\", destruct_body):\n warnings.append(\n \"W8.5: Exception thrown in __destruct(). \"\n \"This can cause unexpected behavior. Catch and log instead.\"\n )\n\n return warnings\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n W8_1_EmptyCatch(),\n W8_2_ErrorSuppression(),\n W8_3_CatchWithOnlyReturn(),\n W8_4_GenericException(),\n W8_5_ThrowInDestructor(),\n]\n",
"structuredPatch": [],
"originalFile": null
}
}