Protokoll #28440

ID28440
Zeitstempel2025-12-28 13:28:45.463419
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens10,067 (Input: 1,197, Output: 8,870)
Dauer95 ms
Request-Zeit2025-12-28 13:28:45.463419
Response-Zeit2025-12-28 13:28:45.558575

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/mcp-servers\/mcp_contracts\/validators\/contract_validator.py",
        "content": "\"\"\"Contract Validator - Orchestriert Contract-Validierung.\"\"\"\n\nimport os\nfrom typing import Optional, Any\n\nfrom domain.contracts import ContractValidationResult\nfrom validators.scope_resolver import ScopeResolver\nfrom validators.rule_evaluator import RuleEvaluator\n\n\nclass ContractValidator:\n    \"\"\"Orchestriert Contract-Validierung mittels ScopeResolver und RuleEvaluator.\"\"\"\n\n    BASE_PATH = \"\/var\/www\/dev.campus.systemische-tools.de\"\n    SUPPORTED_EXTENSIONS = (\".php\", \".js\", \".css\", \".py\")\n\n    def __init__(\n        self,\n        contract_data: dict[str, Any],\n        scope_resolver: Optional[ScopeResolver] = None,\n        rule_evaluator: Optional[RuleEvaluator] = None,\n    ):\n        self.contract_data = contract_data\n        self.contract_name = contract_data.get(\"contract\", {}).get(\"name\", \"unknown\")\n        self.scope_resolver = scope_resolver or ScopeResolver(self.BASE_PATH)\n        self.rule_evaluator = rule_evaluator or RuleEvaluator()\n\n    def validate(self, target_path: Optional[str] = None) -> ContractValidationResult:\n        \"\"\"Fuehrt vollstaendige 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        contract = self.contract_data.get(\"contract\", {})\n        scope = contract.get(\"scope\", {})\n        legacy_applicability = self.contract_data.get(\"applicability\")\n\n        check_paths = self.scope_resolver.resolve_paths(\n            scope, target_path, legacy_applicability\n        )\n\n        if not check_paths:\n            result.findings.append({\"type\": \"info\", \"message\": \"No paths to validate\"})\n            return result\n\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        result.outcome = self.rule_evaluator.determine_outcome(result)\n        return result\n\n    def _validate_directory(self, dir_path: str, result: ContractValidationResult) -> None:\n        \"\"\"Validiert alle Dateien in einem Verzeichnis.\"\"\"\n        for root, _, files in os.walk(dir_path):\n            for f in files:\n                if f.endswith(self.SUPPORTED_EXTENSIONS):\n                    self._validate_file(os.path.join(root, f), 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            contract = self.contract_data.get(\"contract\", {})\n\n            # Rules-basierte Validierung\n            for rule in contract.get(\"rules\", []):\n                in_files = rule.get(\"in_files\", \"**\/*\")\n                if self.scope_resolver.file_matches_pattern(file_path, in_files):\n                    self.rule_evaluator.evaluate_rule(file_path, content, rule, result)\n\n            # Legacy-Formate\n            forbidden = contract.get(\"forbidden\", [])\n            if forbidden:\n                self.rule_evaluator.evaluate_legacy_forbidden(\n                    file_path, content, forbidden, result\n                )\n\n            required = contract.get(\"required\", [])\n            if required:\n                self.rule_evaluator.evaluate_legacy_required(\n                    file_path, content, required, result\n                )\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\ndef run_contract_validation(\n    contract_data: dict[str, Any],\n    target_path: Optional[str] = None,\n) -> ContractValidationResult:\n    \"\"\"Hilfsfunktion fuer Contract-Validierung.\"\"\"\n    validator = ContractValidator(contract_data)\n    return validator.validate(target_path)\n"
    }
}

Response

{
    "tool_response": {
        "type": "update",
        "filePath": "\/var\/www\/mcp-servers\/mcp_contracts\/validators\/contract_validator.py",
        "content": "\"\"\"Contract Validator - Orchestriert Contract-Validierung.\"\"\"\n\nimport os\nfrom typing import Optional, Any\n\nfrom domain.contracts import ContractValidationResult\nfrom validators.scope_resolver import ScopeResolver\nfrom validators.rule_evaluator import RuleEvaluator\n\n\nclass ContractValidator:\n    \"\"\"Orchestriert Contract-Validierung mittels ScopeResolver und RuleEvaluator.\"\"\"\n\n    BASE_PATH = \"\/var\/www\/dev.campus.systemische-tools.de\"\n    SUPPORTED_EXTENSIONS = (\".php\", \".js\", \".css\", \".py\")\n\n    def __init__(\n        self,\n        contract_data: dict[str, Any],\n        scope_resolver: Optional[ScopeResolver] = None,\n        rule_evaluator: Optional[RuleEvaluator] = None,\n    ):\n        self.contract_data = contract_data\n        self.contract_name = contract_data.get(\"contract\", {}).get(\"name\", \"unknown\")\n        self.scope_resolver = scope_resolver or ScopeResolver(self.BASE_PATH)\n        self.rule_evaluator = rule_evaluator or RuleEvaluator()\n\n    def validate(self, target_path: Optional[str] = None) -> ContractValidationResult:\n        \"\"\"Fuehrt vollstaendige 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        contract = self.contract_data.get(\"contract\", {})\n        scope = contract.get(\"scope\", {})\n        legacy_applicability = self.contract_data.get(\"applicability\")\n\n        check_paths = self.scope_resolver.resolve_paths(\n            scope, target_path, legacy_applicability\n        )\n\n        if not check_paths:\n            result.findings.append({\"type\": \"info\", \"message\": \"No paths to validate\"})\n            return result\n\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        result.outcome = self.rule_evaluator.determine_outcome(result)\n        return result\n\n    def _validate_directory(self, dir_path: str, result: ContractValidationResult) -> None:\n        \"\"\"Validiert alle Dateien in einem Verzeichnis.\"\"\"\n        for root, _, files in os.walk(dir_path):\n            for f in files:\n                if f.endswith(self.SUPPORTED_EXTENSIONS):\n                    self._validate_file(os.path.join(root, f), 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            contract = self.contract_data.get(\"contract\", {})\n\n            # Rules-basierte Validierung\n            for rule in contract.get(\"rules\", []):\n                in_files = rule.get(\"in_files\", \"**\/*\")\n                if self.scope_resolver.file_matches_pattern(file_path, in_files):\n                    self.rule_evaluator.evaluate_rule(file_path, content, rule, result)\n\n            # Legacy-Formate\n            forbidden = contract.get(\"forbidden\", [])\n            if forbidden:\n                self.rule_evaluator.evaluate_legacy_forbidden(\n                    file_path, content, forbidden, result\n                )\n\n            required = contract.get(\"required\", [])\n            if required:\n                self.rule_evaluator.evaluate_legacy_required(\n                    file_path, content, required, result\n                )\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\ndef run_contract_validation(\n    contract_data: dict[str, Any],\n    target_path: Optional[str] = None,\n) -> ContractValidationResult:\n    \"\"\"Hilfsfunktion fuer Contract-Validierung.\"\"\"\n    validator = ContractValidator(contract_data)\n    return validator.validate(target_path)\n",
        "structuredPatch": [
            {
                "oldStart": 1,
                "oldLines": 27,
                "newStart": 1,
                "newLines": 32,
                "lines": [
                    "-\"\"\"Contract Validator - YAML-Validierung und Scope-Prüfung\"\"\"",
                    "+\"\"\"Contract Validator - Orchestriert Contract-Validierung.\"\"\"",
                    "+",
                    " import os",
                    "-import re",
                    "-import sys",
                    "-import glob as globlib",
                    "-from typing import Optional, Dict, Any, List",
                    "+from typing import Optional, Any",
                    " ",
                    "-import yaml",
                    "-",
                    "-sys.path.insert(0, \"\/var\/www\/mcp-servers\/mcp_contracts\")",
                    " from domain.contracts import ContractValidationResult",
                    "+from validators.scope_resolver import ScopeResolver",
                    "+from validators.rule_evaluator import RuleEvaluator",
                    " ",
                    " ",
                    " class ContractValidator:",
                    "-    \"\"\"Validiert Contracts gegen ihren definierten Scope\"\"\"",
                    "+    \"\"\"Orchestriert Contract-Validierung mittels ScopeResolver und RuleEvaluator.\"\"\"",
                    " ",
                    "     BASE_PATH = \"\/var\/www\/dev.campus.systemische-tools.de\"",
                    "+    SUPPORTED_EXTENSIONS = (\".php\", \".js\", \".css\", \".py\")",
                    " ",
                    "-    def __init__(self, contract_data: Dict[str, Any]):",
                    "+    def __init__(",
                    "+        self,",
                    "+        contract_data: dict[str, Any],",
                    "+        scope_resolver: Optional[ScopeResolver] = None,",
                    "+        rule_evaluator: Optional[RuleEvaluator] = None,",
                    "+    ):",
                    "         self.contract_data = contract_data",
                    "         self.contract_name = contract_data.get(\"contract\", {}).get(\"name\", \"unknown\")",
                    "+        self.scope_resolver = scope_resolver or ScopeResolver(self.BASE_PATH)",
                    "+        self.rule_evaluator = rule_evaluator or RuleEvaluator()",
                    " ",
                    "     def validate(self, target_path: Optional[str] = None) -> ContractValidationResult:",
                    "-        \"\"\"Führt vollständige Validierung durch\"\"\"",
                    "+        \"\"\"Fuehrt vollstaendige Validierung durch.\"\"\"",
                    "         result = ContractValidationResult(",
                    "             contract=self.contract_name,",
                    "             outcome=\"passed\","
                ]
            },
            {
                "oldStart": 31,
                "oldLines": 17,
                "newStart": 36,
                "newLines": 18,
                "lines": [
                    "             findings=[],",
                    "         )",
                    " ",
                    "-        # Scope ermitteln",
                    "-        check_paths = self._get_check_paths(target_path)",
                    "+        contract = self.contract_data.get(\"contract\", {})",
                    "+        scope = contract.get(\"scope\", {})",
                    "+        legacy_applicability = self.contract_data.get(\"applicability\")",
                    " ",
                    "+        check_paths = self.scope_resolver.resolve_paths(",
                    "+            scope, target_path, legacy_applicability",
                    "+        )",
                    "+",
                    "         if not check_paths:",
                    "-            result.findings.append({",
                    "-                \"type\": \"info\",",
                    "-                \"message\": \"No paths to validate\",",
                    "-            })",
                    "+            result.findings.append({\"type\": \"info\", \"message\": \"No paths to validate\"})",
                    "             return result",
                    " ",
                    "-        # Dateien validieren",
                    "         for check_path in check_paths:",
                    "             if not os.path.exists(check_path):",
                    "                 result.critical += 1"
                ]
            },
            {
                "oldStart": 57,
                "oldLines": 87,
                "newStart": 63,
                "newLines": 43,
                "lines": [
                    "             else:",
                    "                 self._validate_file(check_path, result)",
                    " ",
                    "-        # Outcome bestimmen",
                    "-        result.outcome = self._determine_outcome(result)",
                    "-",
                    "+        result.outcome = self.rule_evaluator.determine_outcome(result)",
                    "         return result",
                    " ",
                    "-    def _get_check_paths(self, target_path: Optional[str] = None) -> List[str]:",
                    "-        \"\"\"Ermittelt zu prüfende Pfade aus Scope (unterstützt alle Formate)\"\"\"",
                    "-        if target_path:",
                    "-            return [target_path]",
                    "-",
                    "-        check_paths = []",
                    "-        contract = self.contract_data.get(\"contract\", {})",
                    "-        scope = contract.get(\"scope\", {})",
                    "-",
                    "-        # Neues Standard-Format: scope.paths",
                    "-        paths_list = scope.get(\"paths\", [])",
                    "-",
                    "-        # Legacy-Formate als Fallback",
                    "-        if not paths_list:",
                    "-            paths_list = scope.get(\"includes\", [])  # Legacy: scope.includes",
                    "-        if not paths_list:",
                    "-            paths_list = scope.get(\"applies_to_paths\", [])  # Legacy",
                    "-        if not paths_list:",
                    "-            # Legacy: applicability.scope",
                    "-            applicability = self.contract_data.get(\"applicability\", {})",
                    "-            paths_list = applicability.get(\"scope\", [])",
                    "-            if isinstance(paths_list, str):",
                    "-                paths_list = [paths_list]",
                    "-",
                    "-        excludes = scope.get(\"excludes\", [])",
                    "-",
                    "-        for pattern in paths_list:",
                    "-            # Glob-Pattern expandieren",
                    "-            full_pattern = os.path.join(self.BASE_PATH, pattern.lstrip(\"\/\"))",
                    "-            matched = globlib.glob(full_pattern, recursive=True)",
                    "-",
                    "-            if matched:",
                    "-                for path in matched:",
                    "-                    # Excludes prüfen",
                    "-                    excluded = False",
                    "-                    for excl in excludes:",
                    "-                        excl_pattern = os.path.join(self.BASE_PATH, excl.lstrip(\"\/\"))",
                    "-                        if globlib.fnmatch.fnmatch(path, excl_pattern):",
                    "-                            excluded = True",
                    "-                            break",
                    "-                    if not excluded:",
                    "-                        check_paths.append(path)",
                    "-            else:",
                    "-                # Falls kein Match, versuche Basisverzeichnis",
                    "-                pattern_path = pattern.replace(\"**\", \"\").replace(\"*\", \"\").rstrip(\"\/\")",
                    "-                full_path = os.path.join(self.BASE_PATH, pattern_path.lstrip(\"\/\"))",
                    "-                if os.path.exists(full_path):",
                    "-                    check_paths.append(full_path)",
                    "-",
                    "-        return list(set(check_paths))  # Duplikate entfernen",
                    "-",
                    "     def _validate_directory(self, dir_path: str, result: ContractValidationResult) -> None:",
                    "-        \"\"\"Validiert alle Dateien in einem Verzeichnis\"\"\"",
                    "-        for root, dirs, files in os.walk(dir_path):",
                    "+        \"\"\"Validiert alle Dateien in einem Verzeichnis.\"\"\"",
                    "+        for root, _, files in os.walk(dir_path):",
                    "             for f in files:",
                    "-                if f.endswith((\".php\", \".js\", \".css\", \".py\")):",
                    "-                    file_path = os.path.join(root, f)",
                    "-                    self._validate_file(file_path, result)",
                    "+                if f.endswith(self.SUPPORTED_EXTENSIONS):",
                    "+                    self._validate_file(os.path.join(root, f), result)",
                    " ",
                    "     def _validate_file(self, file_path: str, result: ContractValidationResult) -> None:",
                    "-        \"\"\"Validiert eine einzelne Datei gegen den Contract\"\"\"",
                    "+        \"\"\"Validiert eine einzelne Datei gegen den Contract.\"\"\"",
                    "         try:",
                    "             with open(file_path, \"r\", encoding=\"utf-8\") as f:",
                    "                 content = f.read()",
                    " ",
                    "-            # Neue Rules-basierte Validierung",
                    "-            rules = self.contract_data.get(\"contract\", {}).get(\"rules\", [])",
                    "-            for rule in rules:",
                    "-                self._check_rule(file_path, content, rule, result)",
                    "+            contract = self.contract_data.get(\"contract\", {})",
                    " ",
                    "-            # Legacy: Forbidden elements prüfen",
                    "-            self._check_forbidden_elements(file_path, content, result)",
                    "+            # Rules-basierte Validierung",
                    "+            for rule in contract.get(\"rules\", []):",
                    "+                in_files = rule.get(\"in_files\", \"**\/*\")",
                    "+                if self.scope_resolver.file_matches_pattern(file_path, in_files):",
                    "+                    self.rule_evaluator.evaluate_rule(file_path, content, rule, result)",
                    " ",
                    "-            # Legacy: Required elements prüfen",
                    "-            self._check_required_elements(file_path, content, result)",
                    "+            # Legacy-Formate",
                    "+            forbidden = contract.get(\"forbidden\", [])",
                    "+            if forbidden:",
                    "+                self.rule_evaluator.evaluate_legacy_forbidden(",
                    "+                    file_path, content, forbidden, result",
                    "+                )",
                    " ",
                    "+            required = contract.get(\"required\", [])",
                    "+            if required:",
                    "+                self.rule_evaluator.evaluate_legacy_required(",
                    "+                    file_path, content, required, result",
                    "+                )",
                    "+",
                    "         except Exception as e:",
                    "             result.minor += 1",
                    "             result.findings.append({"
                ]
            },
            {
                "oldStart": 147,
                "oldLines": 263,
                "newStart": 109,
                "newLines": 11,
                "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:",
                    "-        \"\"\"Prüft auf verbotene Elemente\"\"\"",
                    "-        forbidden = self.contract_data.get(\"contract\", {}).get(\"forbidden\", [])",
                    "-",
                    "-        for item in forbidden:",
                    "-            if isinstance(item, dict):",
                    "-                element = item.get(\"element\", \"\")",
                    "-                severity = item.get(\"severity\", \"minor\")",
                    "-                message = item.get(\"message\", f\"Forbidden element found: {element}\")",
                    "-            else:",
                    "-                element = str(item)",
                    "-                severity = \"minor\"",
                    "-                message = f\"Forbidden element found: {element}\"",
                    "-",
                    "-            if element and element in content:",
                    "-                if severity == \"critical\":",
                    "-                    result.critical += 1",
                    "-                elif severity == \"major\":",
                    "-                    result.major += 1",
                    "-                else:",
                    "-                    result.minor += 1",
                    "-",
                    "-                result.findings.append({",
                    "-                    \"type\": severity,",
                    "-                    \"factor\": \"forbidden_element\",",
                    "-                    \"file\": file_path,",
                    "-                    \"message\": message,",
                    "-                })",
                    "-",
                    "-    def _check_required_elements(",
                    "-        self, file_path: str, content: str, result: ContractValidationResult",
                    "-    ) -> None:",
                    "-        \"\"\"Prüft auf erforderliche Elemente\"\"\"",
                    "-        required = self.contract_data.get(\"contract\", {}).get(\"required\", [])",
                    "-",
                    "-        for item in required:",
                    "-            if isinstance(item, dict):",
                    "-                element = item.get(\"element\", \"\")",
                    "-                severity = item.get(\"severity\", \"major\")",
                    "-                message = item.get(\"message\", f\"Required element missing: {element}\")",
                    "-                applies_to = item.get(\"applies_to\", [])",
                    "-            else:",
                    "-                element = str(item)",
                    "-                severity = \"major\"",
                    "-                message = f\"Required element missing: {element}\"",
                    "-                applies_to = []",
                    "-",
                    "-            # applies_to prüfen (z.B. nur für bestimmte Dateiendungen)",
                    "-            if applies_to:",
                    "-                matches = False",
                    "-                for pattern in applies_to:",
                    "-                    if file_path.endswith(pattern.lstrip(\"*\")):",
                    "-                        matches = True",
                    "-                        break",
                    "-                if not matches:",
                    "-                    continue",
                    "-",
                    "-            if element and element not in content:",
                    "-                if severity == \"critical\":",
                    "-                    result.critical += 1",
                    "-                elif severity == \"major\":",
                    "-                    result.major += 1",
                    "-                else:",
                    "-                    result.minor += 1",
                    "-",
                    "-                result.findings.append({",
                    "-                    \"type\": severity,",
                    "-                    \"factor\": \"required_element\",",
                    "-                    \"file\": file_path,",
                    "-                    \"message\": message,",
                    "-                })",
                    "-",
                    "-    def _determine_outcome(self, result: ContractValidationResult) -> str:",
                    "-        \"\"\"Bestimmt das Validierungsergebnis basierend auf Violations\"\"\"",
                    "-        if result.critical > 0:",
                    "-            return \"rejected\"",
                    "-        elif result.major > 2:",
                    "-            return \"revision_required\"",
                    "-        else:",
                    "-            return \"passed\"",
                    "-",
                    "-",
                    " def run_contract_validation(",
                    "-    contract_data: Dict[str, Any],",
                    "-    target_path: Optional[str] = None",
                    "+    contract_data: dict[str, Any],",
                    "+    target_path: Optional[str] = None,",
                    " ) -> ContractValidationResult:",
                    "-    \"\"\"Hilfsfunktion für Contract-Validierung\"\"\"",
                    "+    \"\"\"Hilfsfunktion fuer Contract-Validierung.\"\"\"",
                    "     validator = ContractValidator(contract_data)",
                    "     return validator.validate(target_path)"
                ]
            }
        ],
        "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, \"\/var\/www\/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            # 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(... [TRUNCATED-c25223228dfd8fdf]"
    }
}
← Vorheriger Zur Liste Nächster →