Protokoll #4325
| ID | 4325 |
|---|---|
| Zeitstempel | 2025-12-22 09:50:43.842514 |
| Client | root |
| IP | Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 |
| Modell | claude-sonnet-4-20250514 |
| Status | completed |
| Tokens | 9,038 (Input: 0, Output: 0) |
| Dauer | 105 ms |
| Request-Zeit | Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18 |
| Response-Zeit | - |
Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22
Request
{
"event": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "\/opt\/mcp-servers\/mcp-contracts\/validators\/contract_validator.py",
"old_string": " def _validate_file(self, file_path: str, result: ContractValidationResult) -> None:\n \"\"\"Validiert eine einzelne Datei gegen den Contract\"\"\"\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n\n # Forbidden elements prüfen\n self._check_forbidden_elements(file_path, content, result)\n\n # Required elements prüfen\n self._check_required_elements(file_path, content, result)\n\n except Exception as e:\n result.minor += 1\n result.findings.append({\n \"type\": \"minor\",\n \"factor\": \"file_read_error\",\n \"file\": file_path,\n \"message\": str(e)[:100],\n })",
"new_string": " def _validate_file(self, file_path: str, result: ContractValidationResult) -> None:\n \"\"\"Validiert eine einzelne Datei gegen den Contract\"\"\"\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n\n # Neue Rules-basierte Validierung\n rules = self.contract_data.get(\"contract\", {}).get(\"rules\", [])\n for rule in rules:\n self._check_rule(file_path, content, rule, result)\n\n # Legacy: Forbidden elements prüfen\n self._check_forbidden_elements(file_path, content, result)\n\n # Legacy: Required elements prüfen\n self._check_required_elements(file_path, content, result)\n\n except Exception as e:\n result.minor += 1\n result.findings.append({\n \"type\": \"minor\",\n \"factor\": \"file_read_error\",\n \"file\": file_path,\n \"message\": str(e)[:100],\n })\n\n def _check_rule(\n self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft eine einzelne Rule gegen eine Datei\"\"\"\n check_type = rule.get(\"check_type\", \"\")\n in_files = rule.get(\"in_files\", \"**\/*\")\n rule_id = rule.get(\"id\", \"unknown\")\n severity = rule.get(\"severity\", \"major\")\n description = rule.get(\"description\", \"\")\n\n # Prüfen ob Datei zum Rule-Pattern passt\n if not self._file_matches_pattern(file_path, in_files):\n return\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 _file_matches_pattern(self, file_path: str, pattern: str) -> bool:\n \"\"\"Prüft ob Datei zum Glob-Pattern passt\"\"\"\n # Relative path from BASE_PATH\n rel_path = file_path.replace(self.BASE_PATH, \"\").lstrip(\"\/\")\n\n # Einfache Pattern-Prüfung\n if \"**\" in pattern:\n # z.B. Controller\/**\/*.php\n parts = pattern.split(\"**\")\n if len(parts) == 2:\n prefix = parts[0].rstrip(\"\/\")\n suffix = parts[1].lstrip(\"\/\")\n if prefix and not rel_path.startswith(prefix.lstrip(\"\/\")):\n return False\n if suffix and not globlib.fnmatch.fnmatch(rel_path, f\"*{suffix}\"):\n return False\n return True\n elif \"*\" in pattern:\n return globlib.fnmatch.fnmatch(rel_path, pattern)\n else:\n return pattern in rel_path\n\n return True\n\n def _check_line_count(\n self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft Zeilenanzahl einer Datei\"\"\"\n max_lines = rule.get(\"max_lines\", 500)\n severity = rule.get(\"severity\", \"major\")\n rule_id = rule.get(\"id\", \"line_count\")\n\n line_count = len(content.splitlines())\n if line_count > max_lines:\n self._add_violation(result, severity, {\n \"rule_id\": rule_id,\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[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf verbotene Patterns\"\"\"\n patterns = rule.get(\"patterns\", [])\n severity = rule.get(\"severity\", \"major\")\n rule_id = rule.get(\"id\", \"forbidden_pattern\")\n description = rule.get(\"description\", \"\")\n\n for pattern in patterns:\n # Versuche als Regex, sonst als String\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 # Zeile finden\n line_no = None\n for i, line in enumerate(content.splitlines(), 1):\n if pattern in line or (re.search(pattern, line) if self._is_regex(pattern) else False):\n line_no = i\n break\n\n self._add_violation(result, severity, {\n \"rule_id\": rule_id,\n \"factor\": \"forbidden_pattern\",\n \"file\": file_path,\n \"message\": f\"Forbidden pattern found: '{pattern}'\",\n \"description\": 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[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf erforderliche Patterns\"\"\"\n patterns = rule.get(\"patterns\", [])\n severity = rule.get(\"severity\", \"major\")\n rule_id = rule.get(\"id\", \"required_pattern\")\n description = rule.get(\"description\", \"\")\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, severity, {\n \"rule_id\": rule_id,\n \"factor\": \"required_pattern\",\n \"file\": file_path,\n \"message\": f\"Required pattern not found: '{pattern}'\",\n \"description\": description,\n })\n\n def _check_dependency(\n self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf verbotene Dependencies\/Imports\"\"\"\n forbidden_imports = rule.get(\"forbidden_imports\", [])\n severity = rule.get(\"severity\", \"critical\")\n rule_id = rule.get(\"id\", \"dependency_check\")\n\n for forbidden in forbidden_imports:\n # PHP: use Statement\n php_pattern = rf\"use\\s+.*{re.escape(forbidden)}\"\n # Python: import Statement\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, severity, {\n \"rule_id\": rule_id,\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 _is_regex(self, pattern: str) -> bool:\n \"\"\"Prüft ob String ein Regex-Pattern ist\"\"\"\n regex_chars = ['[', ']', '(', ')', '\\\\', '^', '$', '.', '+', '?', '{', '}', '|']\n return any(c in pattern for c in regex_chars)\n\n def _add_violation(\n self, result: ContractValidationResult, severity: str, finding: Dict[str, Any]\n ) -> None:\n \"\"\"Fügt 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)"
}
}
Response
{
"tool_response": {
"filePath": "\/opt\/mcp-servers\/mcp-contracts\/validators\/contract_validator.py",
"oldString": " def _validate_file(self, file_path: str, result: ContractValidationResult) -> None:\n \"\"\"Validiert eine einzelne Datei gegen den Contract\"\"\"\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n\n # Forbidden elements prüfen\n self._check_forbidden_elements(file_path, content, result)\n\n # Required elements prüfen\n self._check_required_elements(file_path, content, result)\n\n except Exception as e:\n result.minor += 1\n result.findings.append({\n \"type\": \"minor\",\n \"factor\": \"file_read_error\",\n \"file\": file_path,\n \"message\": str(e)[:100],\n })",
"newString": " def _validate_file(self, file_path: str, result: ContractValidationResult) -> None:\n \"\"\"Validiert eine einzelne Datei gegen den Contract\"\"\"\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n\n # Neue Rules-basierte Validierung\n rules = self.contract_data.get(\"contract\", {}).get(\"rules\", [])\n for rule in rules:\n self._check_rule(file_path, content, rule, result)\n\n # Legacy: Forbidden elements prüfen\n self._check_forbidden_elements(file_path, content, result)\n\n # Legacy: Required elements prüfen\n self._check_required_elements(file_path, content, result)\n\n except Exception as e:\n result.minor += 1\n result.findings.append({\n \"type\": \"minor\",\n \"factor\": \"file_read_error\",\n \"file\": file_path,\n \"message\": str(e)[:100],\n })\n\n def _check_rule(\n self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft eine einzelne Rule gegen eine Datei\"\"\"\n check_type = rule.get(\"check_type\", \"\")\n in_files = rule.get(\"in_files\", \"**\/*\")\n rule_id = rule.get(\"id\", \"unknown\")\n severity = rule.get(\"severity\", \"major\")\n description = rule.get(\"description\", \"\")\n\n # Prüfen ob Datei zum Rule-Pattern passt\n if not self._file_matches_pattern(file_path, in_files):\n return\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 _file_matches_pattern(self, file_path: str, pattern: str) -> bool:\n \"\"\"Prüft ob Datei zum Glob-Pattern passt\"\"\"\n # Relative path from BASE_PATH\n rel_path = file_path.replace(self.BASE_PATH, \"\").lstrip(\"\/\")\n\n # Einfache Pattern-Prüfung\n if \"**\" in pattern:\n # z.B. Controller\/**\/*.php\n parts = pattern.split(\"**\")\n if len(parts) == 2:\n prefix = parts[0].rstrip(\"\/\")\n suffix = parts[1].lstrip(\"\/\")\n if prefix and not rel_path.startswith(prefix.lstrip(\"\/\")):\n return False\n if suffix and not globlib.fnmatch.fnmatch(rel_path, f\"*{suffix}\"):\n return False\n return True\n elif \"*\" in pattern:\n return globlib.fnmatch.fnmatch(rel_path, pattern)\n else:\n return pattern in rel_path\n\n return True\n\n def _check_line_count(\n self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft Zeilenanzahl einer Datei\"\"\"\n max_lines = rule.get(\"max_lines\", 500)\n severity = rule.get(\"severity\", \"major\")\n rule_id = rule.get(\"id\", \"line_count\")\n\n line_count = len(content.splitlines())\n if line_count > max_lines:\n self._add_violation(result, severity, {\n \"rule_id\": rule_id,\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[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf verbotene Patterns\"\"\"\n patterns = rule.get(\"patterns\", [])\n severity = rule.get(\"severity\", \"major\")\n rule_id = rule.get(\"id\", \"forbidden_pattern\")\n description = rule.get(\"description\", \"\")\n\n for pattern in patterns:\n # Versuche als Regex, sonst als String\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 # Zeile finden\n line_no = None\n for i, line in enumerate(content.splitlines(), 1):\n if pattern in line or (re.search(pattern, line) if self._is_regex(pattern) else False):\n line_no = i\n break\n\n self._add_violation(result, severity, {\n \"rule_id\": rule_id,\n \"factor\": \"forbidden_pattern\",\n \"file\": file_path,\n \"message\": f\"Forbidden pattern found: '{pattern}'\",\n \"description\": 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[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf erforderliche Patterns\"\"\"\n patterns = rule.get(\"patterns\", [])\n severity = rule.get(\"severity\", \"major\")\n rule_id = rule.get(\"id\", \"required_pattern\")\n description = rule.get(\"description\", \"\")\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, severity, {\n \"rule_id\": rule_id,\n \"factor\": \"required_pattern\",\n \"file\": file_path,\n \"message\": f\"Required pattern not found: '{pattern}'\",\n \"description\": description,\n })\n\n def _check_dependency(\n self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf verbotene Dependencies\/Imports\"\"\"\n forbidden_imports = rule.get(\"forbidden_imports\", [])\n severity = rule.get(\"severity\", \"critical\")\n rule_id = rule.get(\"id\", \"dependency_check\")\n\n for forbidden in forbidden_imports:\n # PHP: use Statement\n php_pattern = rf\"use\\s+.*{re.escape(forbidden)}\"\n # Python: import Statement\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, severity, {\n \"rule_id\": rule_id,\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 _is_regex(self, pattern: str) -> bool:\n \"\"\"Prüft ob String ein Regex-Pattern ist\"\"\"\n regex_chars = ['[', ']', '(', ')', '\\\\', '^', '$', '.', '+', '?', '{', '}', '|']\n return any(c in pattern for c in regex_chars)\n\n def _add_violation(\n self, result: ContractValidationResult, severity: str, finding: Dict[str, Any]\n ) -> None:\n \"\"\"Fügt 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)",
"originalFile": "\"\"\"Contract Validator - YAML-Validierung und Scope-Prüfung\"\"\"\nimport os\nimport re\nimport sys\nimport glob as globlib\nfrom typing import Optional, Dict, Any, List\n\nimport yaml\n\nsys.path.insert(0, \"\/opt\/mcp-servers\/mcp-contracts\")\nfrom domain.contracts import ContractValidationResult\n\n\nclass ContractValidator:\n \"\"\"Validiert Contracts gegen ihren definierten Scope\"\"\"\n\n BASE_PATH = \"\/var\/www\/dev.campus.systemische-tools.de\"\n\n def __init__(self, contract_data: Dict[str, Any]):\n self.contract_data = contract_data\n self.contract_name = contract_data.get(\"contract\", {}).get(\"name\", \"unknown\")\n\n def validate(self, target_path: Optional[str] = None) -> ContractValidationResult:\n \"\"\"Führt vollständige Validierung durch\"\"\"\n result = ContractValidationResult(\n contract=self.contract_name,\n outcome=\"passed\",\n critical=0,\n major=0,\n minor=0,\n findings=[],\n )\n\n # Scope ermitteln\n check_paths = self._get_check_paths(target_path)\n\n if not check_paths:\n result.findings.append({\n \"type\": \"info\",\n \"message\": \"No paths to validate\",\n })\n return result\n\n # Dateien validieren\n for check_path in check_paths:\n if not os.path.exists(check_path):\n result.critical += 1\n result.findings.append({\n \"type\": \"critical\",\n \"factor\": \"path_existence\",\n \"message\": f\"Path does not exist: {check_path}\",\n })\n continue\n\n if os.path.isdir(check_path):\n self._validate_directory(check_path, result)\n else:\n self._validate_file(check_path, result)\n\n # Outcome bestimmen\n result.outcome = self._determine_outcome(result)\n\n return result\n\n def _get_check_paths(self, target_path: Optional[str] = None) -> List[str]:\n \"\"\"Ermittelt zu prüfende Pfade aus Scope (unterstützt alle Formate)\"\"\"\n if target_path:\n return [target_path]\n\n check_paths = []\n contract = self.contract_data.get(\"contract\", {})\n scope = contract.get(\"scope\", {})\n\n # Neues Standard-Format: scope.paths\n paths_list = scope.get(\"paths\", [])\n\n # Legacy-Formate als Fallback\n if not paths_list:\n paths_list = scope.get(\"includes\", []) # Legacy: scope.includes\n if not paths_list:\n paths_list = scope.get(\"applies_to_paths\", []) # Legacy\n if not paths_list:\n # Legacy: applicability.scope\n applicability = self.contract_data.get(\"applicability\", {})\n paths_list = applicability.get(\"scope\", [])\n if isinstance(paths_list, str):\n paths_list = [paths_list]\n\n excludes = scope.get(\"excludes\", [])\n\n for pattern in paths_list:\n # Glob-Pattern expandieren\n full_pattern = os.path.join(self.BASE_PATH, pattern.lstrip(\"\/\"))\n matched = globlib.glob(full_pattern, recursive=True)\n\n if matched:\n for path in matched:\n # Excludes prüfen\n excluded = False\n for excl in excludes:\n excl_pattern = os.path.join(self.BASE_PATH, excl.lstrip(\"\/\"))\n if globlib.fnmatch.fnmatch(path, excl_pattern):\n excluded = True\n break\n if not excluded:\n check_paths.append(path)\n else:\n # Falls kein Match, versuche Basisverzeichnis\n pattern_path = pattern.replace(\"**\", \"\").replace(\"*\", \"\").rstrip(\"\/\")\n full_path = os.path.join(self.BASE_PATH, pattern_path.lstrip(\"\/\"))\n if os.path.exists(full_path):\n check_paths.append(full_path)\n\n return list(set(check_paths)) # Duplikate entfernen\n\n def _validate_directory(self, dir_path: str, result: ContractValidationResult) -> None:\n \"\"\"Validiert alle Dateien in einem Verzeichnis\"\"\"\n for root, dirs, files in os.walk(dir_path):\n for f in files:\n if f.endswith((\".php\", \".js\", \".css\", \".py\")):\n file_path = os.path.join(root, f)\n self._validate_file(file_path, result)\n\n def _validate_file(self, file_path: str, result: ContractValidationResult) -> None:\n \"\"\"Validiert eine einzelne Datei gegen den Contract\"\"\"\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n content = f.read()\n\n # Forbidden elements prüfen\n self._check_forbidden_elements(file_path, content, result)\n\n # Required elements prüfen\n self._check_required_elements(file_path, content, result)\n\n except Exception as e:\n result.minor += 1\n result.findings.append({\n \"type\": \"minor\",\n \"factor\": \"file_read_error\",\n \"file\": file_path,\n \"message\": str(e)[:100],\n })\n\n def _check_forbidden_elements(\n self, file_path: str, content: str, result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf verbotene Elemente\"\"\"\n forbidden = self.contract_data.get(\"contract\", {}).get(\"forbidden\", [])\n\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 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({\n \"type\": severity,\n \"factor\": \"forbidden_element\",\n \"file\": file_path,\n \"message\": message,\n })\n\n def _check_required_elements(\n self, file_path: str, content: str, result: ContractValidationResult\n ) -> None:\n \"\"\"Prüft auf erforderliche Elemente\"\"\"\n required = self.contract_data.get(\"contract\", {}).get(\"required\", [])\n\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 # applies_to prüfen (z.B. nur für bestimmte Dateiendungen)\n if applies_to:\n matches = False\n for pattern in applies_to:\n if file_path.endswith(pattern.lstrip(\"*\")):\n matches = True\n break\n if not matches:\n continue\n\n if element and element not in content:\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({\n \"type\": 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 else:\n return \"passed\"\n\n\ndef run_contract_validation(\n contract_data: Dict[str, Any],\n target_path: Optional[str] = None\n) -> ContractValidationResult:\n \"\"\"Hilfsfunktion für Contract-Validierung\"\"\"\n validator = ContractValidator(contract_data)\n return validator.validate(target_path)\n",
"structuredPatch": [
{
"oldStart": 127,
"oldLines": 10,
"newStart": 127,
"newLines": 15,
"lines": [
" with open(file_path, \"r\", encoding=\"utf-8\") as f:",
" content = f.read()",
" ",
"- # Forbidden elements prüfen",
"+ # Neue Rules-basierte Validierung",
"+ rules = self.contract_data.get(\"contract\", {}).get(\"rules\", [])",
"+ for rule in rules:",
"+ self._check_rule(file_path, content, rule, result)",
"+",
"+ # Legacy: Forbidden elements prüfen",
" self._check_forbidden_elements(file_path, content, result)",
" ",
"- # Required elements prüfen",
"+ # Legacy: Required elements prüfen",
" self._check_required_elements(file_path, content, result)",
" ",
" except Exception as e:"
]
},
{
"oldStart": 142,
"oldLines": 6,
"newStart": 147,
"newLines": 175,
"lines": [
" \"message\": str(e)[:100],",
" })",
" ",
"+ def _check_rule(",
"+ self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult",
"+ ) -> None:",
"+ \"\"\"Prüft eine einzelne Rule gegen eine Datei\"\"\"",
"+ check_type = rule.get(\"check_type\", \"\")",
"+ in_files = rule.get(\"in_files\", \"**\/*\")",
"+ rule_id = rule.get(\"id\", \"unknown\")",
"+ severity = rule.get(\"severity\", \"major\")",
"+ description = rule.get(\"description\", \"\")",
"+",
"+ # Prüfen ob Datei zum Rule-Pattern passt",
"+ if not self._file_matches_pattern(file_path, in_files):",
"+ return",
"+",
"+ if check_type == \"line_count\":",
"+ self._check_line_count(file_path, content, rule, result)",
"+ elif check_type == \"forbidden_pattern\":",
"+ self._check_forbidden_pattern(file_path, content, rule, result)",
"+ elif check_type == \"required_pattern\":",
"+ self._check_required_pattern(file_path, content, rule, result)",
"+ elif check_type == \"dependency_check\":",
"+ self._check_dependency(file_path, content, rule, result)",
"+",
"+ def _file_matches_pattern(self, file_path: str, pattern: str) -> bool:",
"+ \"\"\"Prüft ob Datei zum Glob-Pattern passt\"\"\"",
"+ # Relative path from BASE_PATH",
"+ rel_path = file_path.replace(self.BASE_PATH, \"\").lstrip(\"\/\")",
"+",
"+ # Einfache Pattern-Prüfung",
"+ if \"**\" in pattern:",
"+ # z.B. Controller\/**\/*.php",
"+ parts = pattern.split(\"**\")",
"+ if len(parts) == 2:",
"+ prefix = parts[0].rstrip(\"\/\")",
"+ suffix = parts[1].lstrip(\"\/\")",
"+ if prefix and not rel_path.startswith(prefix.lstrip(\"\/\")):",
"+ return False",
"+ if suffix and not globlib.fnmatch.fnmatch(rel_path, f\"*{suffix}\"):",
"+ return False",
"+ return True",
"+ elif \"*\" in pattern:",
"+ return globlib.fnmatch.fnmatch(rel_path, pattern)",
"+ else:",
"+ return pattern in rel_path",
"+",
"+ return True",
"+",
"+ def _check_line_count(",
"+ self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult",
"+ ) -> None:",
"+ \"\"\"Prüft Zeilenanzahl einer Datei\"\"\"",
"+ max_lines = rule.get(\"max_lines\", 500)",
"+ severity = rule.get(\"severity\", \"major\")",
"+ rule_id = rule.get(\"id\", \"line_count\")",
"+",
"+ line_count = len(content.splitlines())",
"+ if line_count > max_lines:",
"+ self._add_violation(result, severity, {",
"+ \"rule_id\": rule_id,",
"+ \"factor\": \"line_count\",",
"+ \"file\": file_path,",
"+ \"message\": f\"File has {line_count} lines (max: {max_lines})\",",
"+ \"actual\": line_count,",
"+ \"limit\": max_lines,",
"+ })",
"+",
"+ def _check_forbidden_pattern(",
"+ self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult",
"+ ) -> None:",
"+ \"\"\"Prüft auf verbotene Patterns\"\"\"",
"+ patterns = rule.get(\"patterns\", [])",
"+ severity = rule.get(\"severity\", \"major\")",
"+ rule_id = rule.get(\"id\", \"forbidden_pattern\")",
"+ description = rule.get(\"description\", \"\")",
"+",
"+ for pattern in patterns:",
"+ # Versuche als Regex, sonst als String",
"+ try:",
"+ matches = re.findall(pattern, content)",
"+ except re.error:",
"+ matches = [pattern] if pattern in content else []",
"+",
"+ if matches:",
"+ # Zeile finden",
"+ line_no = None",
"+ for i, line in enumerate(content.splitlines(), 1):",
"+ if pattern in line or (re.search(pattern, line) if self._is_regex(pattern) else False):",
"+ line_no = i",
"+ break",
"+",
"+ self._add_violation(result, severity, {",
"+ \"rule_id\": rule_id,",
"+ \"factor\": \"forbidden_pattern\",",
"+ \"file\": file_path,",
"+ \"message\": f\"Forbidden pattern found: '{pattern}'\",",
"+ \"description\": description,",
"+ \"line\": line_no,",
"+ \"occurrences\": len(matches),",
"+ })",
"+",
"+ def _check_required_pattern(",
"+ self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult",
"+ ) -> None:",
"+ \"\"\"Prüft auf erforderliche Patterns\"\"\"",
"+ patterns = rule.get(\"patterns\", [])",
"+ severity = rule.get(\"severity\", \"major\")",
"+ rule_id = rule.get(\"id\", \"required_pattern\")",
"+ description = rule.get(\"description\", \"\")",
"+",
"+ for pattern in patterns:",
"+ try:",
"+ found = bool(re.search(pattern, content))",
"+ except re.error:",
"+ found = pattern in content",
"+",
"+ if not found:",
"+ self._add_violation(result, severity, {",
"+ \"rule_id\": rule_id,",
"+ \"factor\": \"required_pattern\",",
"+ \"file\": file_path,",
"+ \"message\": f\"Required pattern not found: '{pattern}'\",",
"+ \"description\": description,",
"+ })",
"+",
"+ def _check_dependency(",
"+ self, file_path: str, content: str, rule: Dict[str, Any], result: ContractValidationResult",
"+ ) -> None:",
"+ \"\"\"Prüft auf verbotene Dependencies\/Imports\"\"\"",
"+ forbidden_imports = rule.get(\"forbidden_imports\", [])",
"+ severity = rule.get(\"severity\", \"critical\")",
"+ rule_id = rule.get(\"id\", \"dependency_check\")",
"+",
"+ for forbidden in forbidden_imports:",
"+ # PHP: use Statement",
"+ php_pattern = rf\"use\\s+.*{re.escape(forbidden)}\"",
"+ # Python: import Statement",
"+ py_pattern = rf\"(from|import)\\s+.*{re.escape(forbidden)}\"",
"+",
"+ try:",
"+ if re.search(php_pattern, content) or re.search(py_pattern, content):",
"+ self._add_violation(result, severity, {",
"+ \"rule_id\": rule_id,",
"+ \"factor\": \"forbidden_dependency\",",
"+ \"file\": file_path,",
"+ \"message\": f\"Forbidden import\/dependency: '{forbidden}'\",",
"+ })",
"+ except re.error:",
"+ pass",
"+",
"+ def _is_regex(self, pattern: str) -> bool:",
"+ \"\"\"Prüft ob String ein Regex-Pattern ist\"\"\"",
"+ regex_chars = ['[', ']', '(', ')', '\\\\', '^', '$', '.', '+', '?', '{', '}', '|']",
"+ return any(c in pattern for c in regex_chars)",
"+",
"+ def _add_violation(",
"+ self, result: ContractValidationResult, severity: str, finding: Dict[str, Any]",
"+ ) -> None:",
"+ \"\"\"Fügt eine Violation zum Result hinzu\"\"\"",
"+ finding[\"type\"] = severity",
"+",
"+ if severity == \"critical\":",
"+ result.critical += 1",
"+ elif severity == \"major\":",
"+ result.major += 1",
"+ else:",
"+ result.minor += 1",
"+",
"+ result.findings.append(finding)",
"+",
" def _check_forbidden_elements(",
" self, file_path: str, content: str, result: ContractValidationResult",
" ) -> None:"
]
}
],
"userModified": false,
"replaceAll": false
}
}