{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/mcp-servers\/mcp_contracts\/validators\/rule_evaluator.py",
"content": "\"\"\"\nRule Evaluator - Domain Service fuer Regel-Auswertung.\n\nWICHTIG: Dies ist KEIN Repository!\nKeine CRUD-Operationen, nur Business-Logik.\n\"\"\"\n\nimport re\nfrom typing import Any\n\nfrom domain.contracts import ContractValidationResult\n\n\nclass RuleEvaluator:\n \"\"\"Evaluiert Contract-Regeln gegen Code.\"\"\"\n\n def evaluate_rule(\n self,\n file_path: str,\n content: str,\n rule: dict[str, Any],\n result: ContractValidationResult,\n ) -> None:\n \"\"\"Prueft eine einzelne Rule gegen eine Datei.\"\"\"\n check_type = rule.get(\"check_type\", \"\")\n\n if check_type == \"line_count\":\n self._check_line_count(file_path, content, rule, result)\n elif check_type == \"forbidden_pattern\":\n self._check_forbidden_pattern(file_path, content, rule, result)\n elif check_type == \"required_pattern\":\n self._check_required_pattern(file_path, content, rule, result)\n elif check_type == \"dependency_check\":\n self._check_dependency(file_path, content, rule, result)\n\n def evaluate_legacy_forbidden(\n self,\n file_path: str,\n content: str,\n forbidden: list,\n result: ContractValidationResult,\n ) -> None:\n \"\"\"Prueft auf verbotene Elemente (Legacy-Format).\"\"\"\n for item in forbidden:\n if isinstance(item, dict):\n element = item.get(\"element\", \"\")\n severity = item.get(\"severity\", \"minor\")\n message = item.get(\"message\", f\"Forbidden element found: {element}\")\n else:\n element = str(item)\n severity = \"minor\"\n message = f\"Forbidden element found: {element}\"\n\n if element and element in content:\n self._add_violation(result, severity, {\n \"factor\": \"forbidden_element\",\n \"file\": file_path,\n \"message\": message,\n })\n\n def evaluate_legacy_required(\n self,\n file_path: str,\n content: str,\n required: list,\n result: ContractValidationResult,\n ) -> None:\n \"\"\"Prueft auf erforderliche Elemente (Legacy-Format).\"\"\"\n for item in required:\n if isinstance(item, dict):\n element = item.get(\"element\", \"\")\n severity = item.get(\"severity\", \"major\")\n message = item.get(\"message\", f\"Required element missing: {element}\")\n applies_to = item.get(\"applies_to\", [])\n else:\n element = str(item)\n severity = \"major\"\n message = f\"Required element missing: {element}\"\n applies_to = []\n\n if applies_to:\n matches = any(\n file_path.endswith(p.lstrip(\"*\")) for p in applies_to\n )\n if not matches:\n continue\n\n if element and element not in content:\n self._add_violation(result, severity, {\n \"factor\": \"required_element\",\n \"file\": file_path,\n \"message\": message,\n })\n\n def determine_outcome(self, result: ContractValidationResult) -> str:\n \"\"\"Bestimmt das Validierungsergebnis basierend auf Violations.\"\"\"\n if result.critical > 0:\n return \"rejected\"\n elif result.major > 2:\n return \"revision_required\"\n return \"passed\"\n\n def _check_line_count(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft Zeilenanzahl einer Datei.\"\"\"\n max_lines = rule.get(\"max_lines\", 500)\n line_count = len(content.splitlines())\n\n if line_count > max_lines:\n self._add_violation(result, rule.get(\"severity\", \"major\"), {\n \"rule_id\": rule.get(\"id\", \"line_count\"),\n \"factor\": \"line_count\",\n \"file\": file_path,\n \"message\": f\"File has {line_count} lines (max: {max_lines})\",\n \"actual\": line_count,\n \"limit\": max_lines,\n })\n\n def _check_forbidden_pattern(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft auf verbotene Patterns.\"\"\"\n patterns = rule.get(\"patterns\", [])\n\n for pattern in patterns:\n try:\n matches = re.findall(pattern, content)\n except re.error:\n matches = [pattern] if pattern in content else []\n\n if matches:\n line_no = self._find_line_number(content, pattern)\n self._add_violation(result, rule.get(\"severity\", \"major\"), {\n \"rule_id\": rule.get(\"id\", \"forbidden_pattern\"),\n \"factor\": \"forbidden_pattern\",\n \"file\": file_path,\n \"message\": f\"Forbidden pattern found: '{pattern}'\",\n \"description\": rule.get(\"description\", \"\"),\n \"line\": line_no,\n \"occurrences\": len(matches),\n })\n\n def _check_required_pattern(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft auf erforderliche Patterns.\"\"\"\n patterns = rule.get(\"patterns\", [])\n\n for pattern in patterns:\n try:\n found = bool(re.search(pattern, content))\n except re.error:\n found = pattern in content\n\n if not found:\n self._add_violation(result, rule.get(\"severity\", \"major\"), {\n \"rule_id\": rule.get(\"id\", \"required_pattern\"),\n \"factor\": \"required_pattern\",\n \"file\": file_path,\n \"message\": f\"Required pattern not found: '{pattern}'\",\n \"description\": rule.get(\"description\", \"\"),\n })\n\n def _check_dependency(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft auf verbotene Dependencies\/Imports.\"\"\"\n forbidden_imports = rule.get(\"forbidden_imports\", [])\n\n for forbidden in forbidden_imports:\n php_pattern = rf\"use\\s+.*{re.escape(forbidden)}\"\n py_pattern = rf\"(from|import)\\s+.*{re.escape(forbidden)}\"\n\n try:\n if re.search(php_pattern, content) or re.search(py_pattern, content):\n self._add_violation(result, rule.get(\"severity\", \"critical\"), {\n \"rule_id\": rule.get(\"id\", \"dependency_check\"),\n \"factor\": \"forbidden_dependency\",\n \"file\": file_path,\n \"message\": f\"Forbidden import\/dependency: '{forbidden}'\",\n })\n except re.error:\n pass\n\n def _find_line_number(self, content: str, pattern: str) -> int | None:\n \"\"\"Findet die Zeilennummer eines Patterns.\"\"\"\n for i, line in enumerate(content.splitlines(), 1):\n if pattern in line:\n return i\n try:\n if re.search(pattern, line):\n return i\n except re.error:\n pass\n return None\n\n def _add_violation(\n self, result: ContractValidationResult, severity: str, finding: dict\n ) -> None:\n \"\"\"Fuegt eine Violation zum Result hinzu.\"\"\"\n finding[\"type\"] = severity\n\n if severity == \"critical\":\n result.critical += 1\n elif severity == \"major\":\n result.major += 1\n else:\n result.minor += 1\n\n result.findings.append(finding)\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/mcp-servers\/mcp_contracts\/validators\/rule_evaluator.py",
"content": "\"\"\"\nRule Evaluator - Domain Service fuer Regel-Auswertung.\n\nWICHTIG: Dies ist KEIN Repository!\nKeine CRUD-Operationen, nur Business-Logik.\n\"\"\"\n\nimport re\nfrom typing import Any\n\nfrom domain.contracts import ContractValidationResult\n\n\nclass RuleEvaluator:\n \"\"\"Evaluiert Contract-Regeln gegen Code.\"\"\"\n\n def evaluate_rule(\n self,\n file_path: str,\n content: str,\n rule: dict[str, Any],\n result: ContractValidationResult,\n ) -> None:\n \"\"\"Prueft eine einzelne Rule gegen eine Datei.\"\"\"\n check_type = rule.get(\"check_type\", \"\")\n\n if check_type == \"line_count\":\n self._check_line_count(file_path, content, rule, result)\n elif check_type == \"forbidden_pattern\":\n self._check_forbidden_pattern(file_path, content, rule, result)\n elif check_type == \"required_pattern\":\n self._check_required_pattern(file_path, content, rule, result)\n elif check_type == \"dependency_check\":\n self._check_dependency(file_path, content, rule, result)\n\n def evaluate_legacy_forbidden(\n self,\n file_path: str,\n content: str,\n forbidden: list,\n result: ContractValidationResult,\n ) -> None:\n \"\"\"Prueft auf verbotene Elemente (Legacy-Format).\"\"\"\n for item in forbidden:\n if isinstance(item, dict):\n element = item.get(\"element\", \"\")\n severity = item.get(\"severity\", \"minor\")\n message = item.get(\"message\", f\"Forbidden element found: {element}\")\n else:\n element = str(item)\n severity = \"minor\"\n message = f\"Forbidden element found: {element}\"\n\n if element and element in content:\n self._add_violation(result, severity, {\n \"factor\": \"forbidden_element\",\n \"file\": file_path,\n \"message\": message,\n })\n\n def evaluate_legacy_required(\n self,\n file_path: str,\n content: str,\n required: list,\n result: ContractValidationResult,\n ) -> None:\n \"\"\"Prueft auf erforderliche Elemente (Legacy-Format).\"\"\"\n for item in required:\n if isinstance(item, dict):\n element = item.get(\"element\", \"\")\n severity = item.get(\"severity\", \"major\")\n message = item.get(\"message\", f\"Required element missing: {element}\")\n applies_to = item.get(\"applies_to\", [])\n else:\n element = str(item)\n severity = \"major\"\n message = f\"Required element missing: {element}\"\n applies_to = []\n\n if applies_to:\n matches = any(\n file_path.endswith(p.lstrip(\"*\")) for p in applies_to\n )\n if not matches:\n continue\n\n if element and element not in content:\n self._add_violation(result, severity, {\n \"factor\": \"required_element\",\n \"file\": file_path,\n \"message\": message,\n })\n\n def determine_outcome(self, result: ContractValidationResult) -> str:\n \"\"\"Bestimmt das Validierungsergebnis basierend auf Violations.\"\"\"\n if result.critical > 0:\n return \"rejected\"\n elif result.major > 2:\n return \"revision_required\"\n return \"passed\"\n\n def _check_line_count(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft Zeilenanzahl einer Datei.\"\"\"\n max_lines = rule.get(\"max_lines\", 500)\n line_count = len(content.splitlines())\n\n if line_count > max_lines:\n self._add_violation(result, rule.get(\"severity\", \"major\"), {\n \"rule_id\": rule.get(\"id\", \"line_count\"),\n \"factor\": \"line_count\",\n \"file\": file_path,\n \"message\": f\"File has {line_count} lines (max: {max_lines})\",\n \"actual\": line_count,\n \"limit\": max_lines,\n })\n\n def _check_forbidden_pattern(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft auf verbotene Patterns.\"\"\"\n patterns = rule.get(\"patterns\", [])\n\n for pattern in patterns:\n try:\n matches = re.findall(pattern, content)\n except re.error:\n matches = [pattern] if pattern in content else []\n\n if matches:\n line_no = self._find_line_number(content, pattern)\n self._add_violation(result, rule.get(\"severity\", \"major\"), {\n \"rule_id\": rule.get(\"id\", \"forbidden_pattern\"),\n \"factor\": \"forbidden_pattern\",\n \"file\": file_path,\n \"message\": f\"Forbidden pattern found: '{pattern}'\",\n \"description\": rule.get(\"description\", \"\"),\n \"line\": line_no,\n \"occurrences\": len(matches),\n })\n\n def _check_required_pattern(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft auf erforderliche Patterns.\"\"\"\n patterns = rule.get(\"patterns\", [])\n\n for pattern in patterns:\n try:\n found = bool(re.search(pattern, content))\n except re.error:\n found = pattern in content\n\n if not found:\n self._add_violation(result, rule.get(\"severity\", \"major\"), {\n \"rule_id\": rule.get(\"id\", \"required_pattern\"),\n \"factor\": \"required_pattern\",\n \"file\": file_path,\n \"message\": f\"Required pattern not found: '{pattern}'\",\n \"description\": rule.get(\"description\", \"\"),\n })\n\n def _check_dependency(\n self, file_path: str, content: str, rule: dict, result: ContractValidationResult\n ) -> None:\n \"\"\"Prueft auf verbotene Dependencies\/Imports.\"\"\"\n forbidden_imports = rule.get(\"forbidden_imports\", [])\n\n for forbidden in forbidden_imports:\n php_pattern = rf\"use\\s+.*{re.escape(forbidden)}\"\n py_pattern = rf\"(from|import)\\s+.*{re.escape(forbidden)}\"\n\n try:\n if re.search(php_pattern, content) or re.search(py_pattern, content):\n self._add_violation(result, rule.get(\"severity\", \"critical\"), {\n \"rule_id\": rule.get(\"id\", \"dependency_check\"),\n \"factor\": \"forbidden_dependency\",\n \"file\": file_path,\n \"message\": f\"Forbidden import\/dependency: '{forbidden}'\",\n })\n except re.error:\n pass\n\n def _find_line_number(self, content: str, pattern: str) -> int | None:\n \"\"\"Findet die Zeilennummer eines Patterns.\"\"\"\n for i, line in enumerate(content.splitlines(), 1):\n if pattern in line:\n return i\n try:\n if re.search(pattern, line):\n return i\n except re.error:\n pass\n return None\n\n def _add_violation(\n self, result: ContractValidationResult, severity: str, finding: dict\n ) -> None:\n \"\"\"Fuegt eine Violation zum Result hinzu.\"\"\"\n finding[\"type\"] = severity\n\n if severity == \"critical\":\n result.critical += 1\n elif severity == \"major\":\n result.major += 1\n else:\n result.minor += 1\n\n result.findings.append(finding)\n",
"structuredPatch": [],
"originalFile": null
}
}