Protokoll #15799

ID15799
Zeitstempel2025-12-25 16:56:54.932862
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens3,983 (Input: 1,989, Output: 1,994)
Dauer115 ms
Request-Zeit2025-12-25 16:56:54.932862
Response-Zeit2025-12-25 16:56:55.047883

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_security.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook Security Regeln (BLOCK) - MVC + Layer Violations + DIP.\n\nP2.x Regeln: MVC-Trennung, keine SQL in Controller\nP6.x Regeln: SOLID + DIP Layer-Violations\nH-Regeln: Legacy Architecture Guards\n\"\"\"\n\nimport re\nfrom typing import Optional\nfrom .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist\n\n\n# =============================================================================\n# ALLOWLIST\n# =============================================================================\n\nFACTORY_ALLOWLIST = [\n    \"\/Factory\/\",\n    \"\/Bootstrap\/\",\n]\n\n\n# =============================================================================\n# HELPER FUNCTIONS\n# =============================================================================\n\ndef block(rule_id: str, message: str) -> dict:\n    \"\"\"Erzeugt Block-Response.\"\"\"\n    return {\n        \"allowed\": False,\n        \"message\": f\"QUALITY VIOLATION [{rule_id}]: {message}\"\n    }\n\n\n# =============================================================================\n# PRÜFUNG 2: MVC + CRUD\n# =============================================================================\n\ndef p2_1_no_sql_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.1: Keine SQL-Statements in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    sql_patterns = [\n        r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n        r\"\\bINSERT\\s+INTO\\b\",\n        r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n        r\"\\bDELETE\\s+FROM\\b\",\n    ]\n\n    for pattern in sql_patterns:\n        if re.search(pattern, content, re.IGNORECASE):\n            return block(\"P2.1\", \"SQL statement in Controller. Move to Repository.\")\n\n    return None\n\n\ndef p2_2_no_transactions_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.2: Keine Transaktionen in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    transaction_patterns = [\n        r\"\\bbeginTransaction\\s*\\(\",\n        r\"\\bcommit\\s*\\(\",\n        r\"\\brollBack\\s*\\(\",\n        r\"\\brollback\\s*\\(\",\n    ]\n\n    for pattern in transaction_patterns:\n        if re.search(pattern, content):\n            return block(\"P2.2\", \"Transaction control in Controller. Move to UseCase or Repository.\")\n\n    return None\n\n\ndef p2_3_no_echo_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.3: Kein echo\/print in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    output_patterns = [\n        r\"\\becho\\s+\",\n        r\"\\becho\\(\",\n        r\"\\bprint\\s+\",\n        r\"\\bprint\\(\",\n    ]\n\n    for pattern in output_patterns:\n        if re.search(pattern, content):\n            return block(\"P2.3\", \"Direct output (echo\/print) in Controller. Use Response object.\")\n\n    return None\n\n\ndef p2_4_no_db_in_usecases(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.4: Keine DB-Artefakte in UseCases.\"\"\"\n    if \"\/UseCases\/\" not in file_path and \"\/Application\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    db_patterns = [\n        r\"\\bnew\\s+PDO\\b\",\n        r\"\\bPDO::\",\n        r\"\\bDatabaseFactory::\",\n        r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n        r\"\\bINSERT\\s+INTO\\b\",\n        r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n        r\"\\bDELETE\\s+FROM\\b\",\n    ]\n\n    for pattern in db_patterns:\n        if re.search(pattern, content, re.IGNORECASE):\n            return block(\"P2.4\", \"DB artifact in UseCase\/Application. Use Repository interface.\")\n\n    return None\n\n\n# =============================================================================\n# PRÜFUNG 6: SOLID + DIP\n# =============================================================================\n\ndef p6_1_application_no_infrastructure_import(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P6.1: UseCases\/Application darf Infrastructure nicht importieren.\"\"\"\n    if \"\/UseCases\/\" not in file_path and \"\/Application\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"use\\s+Infrastructure\\\\\", content):\n        return block(\"P6.1\", \"Application layer must not import Infrastructure directly (DIP)\")\n\n    return None\n\n\ndef p6_2_domain_no_application_import(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P6.2: Domain darf Application-Layer nicht importieren.\"\"\"\n    if \"\/Domain\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"use\\s+(UseCases|Application)\\\\\", content):\n        return block(\"P6.2\", \"Domain must not import Application layer (layer violation)\")\n\n    return None\n\n\n# =============================================================================\n# LEGACY H-REGELN (aus architecture_guard.py)\n# =============================================================================\n\ndef h2_domain_no_infrastructure(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H2: Domain ohne Infrastructure.\"\"\"\n    if \"\/Domain\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"use\\s+Infrastructure\\\\\", content):\n        return block(\"H2\", \"Domain must not use Infrastructure\")\n\n    return None\n\n\ndef h3_database_factory_only_in_factory(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H3: DatabaseFactory nur in Factory.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST + FACTORY_ALLOWLIST):\n        return None\n\n    if re.search(r\"DatabaseFactory::\", content):\n        return block(\"H3\", \"DatabaseFactory only allowed in Factory classes\")\n\n    return None\n\n\ndef h4_no_new_repository_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H4: Kein new Repository in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"new\\s+\\w+Repository\\s*\\(\", content):\n        return block(\"H4\", \"new Repository in Controller not allowed. Use DI.\")\n\n    return None\n\n\ndef h5_no_new_infrastructure_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H5: Kein new Infrastructure in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"new\\s+Infrastructure\\\\\", content):\n        return block(\"H5\", \"new Infrastructure in Controller not allowed. Use DI.\")\n\n    return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    # Prüfung 2: MVC + CRUD\n    p2_1_no_sql_in_controller,\n    p2_2_no_transactions_in_controller,\n    p2_3_no_echo_in_controller,\n    p2_4_no_db_in_usecases,\n\n    # Prüfung 6: SOLID + DIP\n    p6_1_application_no_infrastructure_import,\n    p6_2_domain_no_application_import,\n\n    # Legacy H-Regeln\n    h2_domain_no_infrastructure,\n    h3_database_factory_only_in_factory,\n    h4_no_new_repository_in_controller,\n    h5_no_new_infrastructure_in_controller,\n]\n"
    }
}

Response

{
    "tool_response": {
        "type": "create",
        "filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_security.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook Security Regeln (BLOCK) - MVC + Layer Violations + DIP.\n\nP2.x Regeln: MVC-Trennung, keine SQL in Controller\nP6.x Regeln: SOLID + DIP Layer-Violations\nH-Regeln: Legacy Architecture Guards\n\"\"\"\n\nimport re\nfrom typing import Optional\nfrom .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist\n\n\n# =============================================================================\n# ALLOWLIST\n# =============================================================================\n\nFACTORY_ALLOWLIST = [\n    \"\/Factory\/\",\n    \"\/Bootstrap\/\",\n]\n\n\n# =============================================================================\n# HELPER FUNCTIONS\n# =============================================================================\n\ndef block(rule_id: str, message: str) -> dict:\n    \"\"\"Erzeugt Block-Response.\"\"\"\n    return {\n        \"allowed\": False,\n        \"message\": f\"QUALITY VIOLATION [{rule_id}]: {message}\"\n    }\n\n\n# =============================================================================\n# PRÜFUNG 2: MVC + CRUD\n# =============================================================================\n\ndef p2_1_no_sql_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.1: Keine SQL-Statements in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    sql_patterns = [\n        r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n        r\"\\bINSERT\\s+INTO\\b\",\n        r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n        r\"\\bDELETE\\s+FROM\\b\",\n    ]\n\n    for pattern in sql_patterns:\n        if re.search(pattern, content, re.IGNORECASE):\n            return block(\"P2.1\", \"SQL statement in Controller. Move to Repository.\")\n\n    return None\n\n\ndef p2_2_no_transactions_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.2: Keine Transaktionen in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    transaction_patterns = [\n        r\"\\bbeginTransaction\\s*\\(\",\n        r\"\\bcommit\\s*\\(\",\n        r\"\\brollBack\\s*\\(\",\n        r\"\\brollback\\s*\\(\",\n    ]\n\n    for pattern in transaction_patterns:\n        if re.search(pattern, content):\n            return block(\"P2.2\", \"Transaction control in Controller. Move to UseCase or Repository.\")\n\n    return None\n\n\ndef p2_3_no_echo_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.3: Kein echo\/print in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    output_patterns = [\n        r\"\\becho\\s+\",\n        r\"\\becho\\(\",\n        r\"\\bprint\\s+\",\n        r\"\\bprint\\(\",\n    ]\n\n    for pattern in output_patterns:\n        if re.search(pattern, content):\n            return block(\"P2.3\", \"Direct output (echo\/print) in Controller. Use Response object.\")\n\n    return None\n\n\ndef p2_4_no_db_in_usecases(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P2.4: Keine DB-Artefakte in UseCases.\"\"\"\n    if \"\/UseCases\/\" not in file_path and \"\/Application\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    db_patterns = [\n        r\"\\bnew\\s+PDO\\b\",\n        r\"\\bPDO::\",\n        r\"\\bDatabaseFactory::\",\n        r\"\\bSELECT\\s+.+\\s+FROM\\b\",\n        r\"\\bINSERT\\s+INTO\\b\",\n        r\"\\bUPDATE\\s+\\w+\\s+SET\\b\",\n        r\"\\bDELETE\\s+FROM\\b\",\n    ]\n\n    for pattern in db_patterns:\n        if re.search(pattern, content, re.IGNORECASE):\n            return block(\"P2.4\", \"DB artifact in UseCase\/Application. Use Repository interface.\")\n\n    return None\n\n\n# =============================================================================\n# PRÜFUNG 6: SOLID + DIP\n# =============================================================================\n\ndef p6_1_application_no_infrastructure_import(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P6.1: UseCases\/Application darf Infrastructure nicht importieren.\"\"\"\n    if \"\/UseCases\/\" not in file_path and \"\/Application\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"use\\s+Infrastructure\\\\\", content):\n        return block(\"P6.1\", \"Application layer must not import Infrastructure directly (DIP)\")\n\n    return None\n\n\ndef p6_2_domain_no_application_import(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P6.2: Domain darf Application-Layer nicht importieren.\"\"\"\n    if \"\/Domain\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"use\\s+(UseCases|Application)\\\\\", content):\n        return block(\"P6.2\", \"Domain must not import Application layer (layer violation)\")\n\n    return None\n\n\n# =============================================================================\n# LEGACY H-REGELN (aus architecture_guard.py)\n# =============================================================================\n\ndef h2_domain_no_infrastructure(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H2: Domain ohne Infrastructure.\"\"\"\n    if \"\/Domain\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"use\\s+Infrastructure\\\\\", content):\n        return block(\"H2\", \"Domain must not use Infrastructure\")\n\n    return None\n\n\ndef h3_database_factory_only_in_factory(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H3: DatabaseFactory nur in Factory.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST + FACTORY_ALLOWLIST):\n        return None\n\n    if re.search(r\"DatabaseFactory::\", content):\n        return block(\"H3\", \"DatabaseFactory only allowed in Factory classes\")\n\n    return None\n\n\ndef h4_no_new_repository_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H4: Kein new Repository in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"new\\s+\\w+Repository\\s*\\(\", content):\n        return block(\"H4\", \"new Repository in Controller not allowed. Use DI.\")\n\n    return None\n\n\ndef h5_no_new_infrastructure_in_controller(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"H5: Kein new Infrastructure in Controller.\"\"\"\n    if \"\/Controller\/\" not in file_path:\n        return None\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    if re.search(r\"new\\s+Infrastructure\\\\\", content):\n        return block(\"H5\", \"new Infrastructure in Controller not allowed. Use DI.\")\n\n    return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    # Prüfung 2: MVC + CRUD\n    p2_1_no_sql_in_controller,\n    p2_2_no_transactions_in_controller,\n    p2_3_no_echo_in_controller,\n    p2_4_no_db_in_usecases,\n\n    # Prüfung 6: SOLID + DIP\n    p6_1_application_no_infrastructure_import,\n    p6_2_domain_no_application_import,\n\n    # Legacy H-Regeln\n    h2_domain_no_infrastructure,\n    h3_database_factory_only_in_factory,\n    h4_no_new_repository_in_controller,\n    h5_no_new_infrastructure_in_controller,\n]\n",
        "structuredPatch": [],
        "originalFile": null
    }
}
← Vorheriger Zur Liste Nächster →