Protokoll #31314

ID31314
Zeitstempel2025-12-31 03:11:55.937873
Clientroot
IP145.224.72.245
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens2,543 (Input: 1,269, Output: 1,274)
Dauer95 ms
Request-Zeit2025-12-31 03:11:55.937873
Response-Zeit2025-12-31 03:11:56.033059

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/scripts\/hooks\/block_password_exposure.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nClaude Code Hook: Blockiert Passwort-Exposition in Bash-Commands\n\nHook Type: PreToolUse\nBlockiert: Bash-Befehle die Passwörter im Klartext enthalten\nEmpfehlung: Passwörter aus \/etc\/environment oder .env laden\n\nPatterns die blockiert werden:\n- DB_PASSWORD=\"wert\"\n- MARIADB_ROOT_PASSWORD=\"wert\"\n- PASSWORD=\"wert\"\n- --password=wert\n- -p\"wert\" oder -p'wert'\n\"\"\"\nimport json\nimport re\nimport sys\n\n\n# Patterns die Passwort-Exposition erkennen\nBLOCKED_PATTERNS = [\n    # Environment variable assignments with actual values\n    r'DB_PASSWORD=[\"\\'][^\"\\']+[\"\\']',              # DB_PASSWORD=\"value\"\n    r'DB_PASSWORD=[^\\s\"\\']+\\s',                     # DB_PASSWORD=value (unquoted)\n    r'MARIADB_ROOT_PASSWORD=[\"\\'][^\"\\']+[\"\\']',    # MARIADB_ROOT_PASSWORD=\"value\"\n    r'MARIADB_ROOT_PASSWORD=[^\\s\"\\']+\\s',           # MARIADB_ROOT_PASSWORD=value\n    r'MYSQL_ROOT_PASSWORD=[\"\\'][^\"\\']+[\"\\']',      # MYSQL_ROOT_PASSWORD=\"value\"\n    r'MYSQL_PASSWORD=[\"\\'][^\"\\']+[\"\\']',           # MYSQL_PASSWORD=\"value\"\n    r'API_KEY=[\"\\'][^\"\\']+[\"\\']',                  # API_KEY=\"value\"\n    r'ANTHROPIC_API_KEY=[\"\\'][^\"\\']+[\"\\']',        # ANTHROPIC_API_KEY=\"value\"\n    r'SECRET=[\"\\'][^\"\\']+[\"\\']',                   # SECRET=\"value\"\n    r'TOKEN=[\"\\'][^\"\\']+[\"\\']',                    # TOKEN=\"value\"\n    # Generic PASSWORD with value (but not empty)\n    r'PASSWORD=[\"\\'][^\"\\']+[\"\\']',                 # PASSWORD=\"value\"\n]\n\n# Patterns die erlaubt sind (false positives vermeiden)\nALLOWED_PATTERNS = [\n    r'\\$DB_PASSWORD',                               # Using variable $DB_PASSWORD\n    r'\\$\\{DB_PASSWORD\\}',                          # Using ${DB_PASSWORD}\n    r'DB_PASSWORD=\\$',                              # DB_PASSWORD=$OTHER_VAR\n    r'DB_PASSWORD=\"\"',                              # Empty assignment\n    r\"DB_PASSWORD=''\",                              # Empty assignment\n    r'getenv\\([\"\\']DB_PASSWORD',                   # PHP getenv\n    r'os\\.environ\\.get\\([\"\\']DB_PASSWORD',         # Python os.environ\n    r'get_db_password\\(\\)',                        # Calling getter function\n]\n\nERROR_MESSAGE = \"\"\"\nBLOCKIERT: Passwort-Exposition im Bash-Command erkannt!\n\nDu hast versucht, ein Passwort im Klartext in einem Bash-Befehl zu übergeben.\nDas exponiert das Passwort in:\n  - ps aux (für alle User sichtbar)\n  - \/proc\/*\/cmdline\n  - bash_history\n  - Konversations-Logs\n\nRICHTIG: Passwort aus Environment laden\n  Das Passwort ist bereits in \/etc\/environment gesetzt.\n  Einfach das Script ohne Passwort-Übergabe starten:\n\n  # RICHTIG:\n  .\/venv\/bin\/python script.py\n\n  # FALSCH:\n  DB_PASSWORD=\"xxx\" .\/venv\/bin\/python script.py\n\n  Das Script lädt das Passwort automatisch via:\n    from config import get_db_password\n    password = get_db_password()\n\nDokumentation: \/docs\/betrieb\/secrets\n\"\"\".strip()\n\n\ndef check_command(command: str) -> tuple[bool, str]:\n    \"\"\"\n    Prüft ob Befehl Passwort-Exposition enthält.\n\n    Returns:\n        (allowed, error_message)\n    \"\"\"\n    # Erst prüfen ob es ein erlaubtes Pattern ist\n    for pattern in ALLOWED_PATTERNS:\n        if re.search(pattern, command, re.IGNORECASE):\n            return True, \"\"\n\n    # Dann prüfen ob blockiertes Pattern vorhanden\n    for pattern in BLOCKED_PATTERNS:\n        if re.search(pattern, command, re.IGNORECASE):\n            return False, ERROR_MESSAGE\n\n    return True, \"\"\n\n\ndef main():\n    \"\"\"\n    Hook Entry Point (PreToolUse).\n\n    Input (stdin): JSON mit tool_name und tool_input\n    Output (stdout): JSON mit allow\/deny\n    \"\"\"\n    try:\n        # Lese Hook-Input von stdin\n        hook_input = json.loads(sys.stdin.read())\n\n        tool_name = hook_input.get(\"tool_name\", \"\")\n        tool_input = hook_input.get(\"tool_input\", {})\n\n        # Nur Bash-Tool prüfen\n        if tool_name != \"Bash\":\n            sys.exit(0)\n\n        # Befehl extrahieren\n        command = tool_input.get(\"command\", \"\")\n\n        # Prüfen ob Befehl blockiert werden soll\n        allowed, error_msg = check_command(command)\n\n        if not allowed:\n            # Output im Claude Code Format\n            output = {\n                \"hookSpecificOutput\": {\n                    \"hookEventName\": \"PreToolUse\",\n                    \"permissionDecision\": \"deny\",\n                    \"permissionDecisionReason\": error_msg\n                }\n            }\n            print(json.dumps(output))\n            sys.exit(1)\n\n        # Allowed: kein Output nötig\n        sys.exit(0)\n\n    except Exception as e:\n        # Bei Fehler: Fail-Open (durchlassen, aber warnen)\n        print(f\"Hook error (fail-open): {e}\", file=sys.stderr)\n        sys.exit(0)\n\n\nif __name__ == \"__main__\":\n    main()\n"
    }
}

Response

{
    "tool_response": {
        "type": "create",
        "filePath": "\/var\/www\/scripts\/hooks\/block_password_exposure.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nClaude Code Hook: Blockiert Passwort-Exposition in Bash-Commands\n\nHook Type: PreToolUse\nBlockiert: Bash-Befehle die Passwörter im Klartext enthalten\nEmpfehlung: Passwörter aus \/etc\/environment oder .env laden\n\nPatterns die blockiert werden:\n- DB_PASSWORD=\"wert\"\n- MARIADB_ROOT_PASSWORD=\"wert\"\n- PASSWORD=\"wert\"\n- --password=wert\n- -p\"wert\" oder -p'wert'\n\"\"\"\nimport json\nimport re\nimport sys\n\n\n# Patterns die Passwort-Exposition erkennen\nBLOCKED_PATTERNS = [\n    # Environment variable assignments with actual values\n    r'DB_PASSWORD=[\"\\'][^\"\\']+[\"\\']',              # DB_PASSWORD=\"value\"\n    r'DB_PASSWORD=[^\\s\"\\']+\\s',                     # DB_PASSWORD=value (unquoted)\n    r'MARIADB_ROOT_PASSWORD=[\"\\'][^\"\\']+[\"\\']',    # MARIADB_ROOT_PASSWORD=\"value\"\n    r'MARIADB_ROOT_PASSWORD=[^\\s\"\\']+\\s',           # MARIADB_ROOT_PASSWORD=value\n    r'MYSQL_ROOT_PASSWORD=[\"\\'][^\"\\']+[\"\\']',      # MYSQL_ROOT_PASSWORD=\"value\"\n    r'MYSQL_PASSWORD=[\"\\'][^\"\\']+[\"\\']',           # MYSQL_PASSWORD=\"value\"\n    r'API_KEY=[\"\\'][^\"\\']+[\"\\']',                  # API_KEY=\"value\"\n    r'ANTHROPIC_API_KEY=[\"\\'][^\"\\']+[\"\\']',        # ANTHROPIC_API_KEY=\"value\"\n    r'SECRET=[\"\\'][^\"\\']+[\"\\']',                   # SECRET=\"value\"\n    r'TOKEN=[\"\\'][^\"\\']+[\"\\']',                    # TOKEN=\"value\"\n    # Generic PASSWORD with value (but not empty)\n    r'PASSWORD=[\"\\'][^\"\\']+[\"\\']',                 # PASSWORD=\"value\"\n]\n\n# Patterns die erlaubt sind (false positives vermeiden)\nALLOWED_PATTERNS = [\n    r'\\$DB_PASSWORD',                               # Using variable $DB_PASSWORD\n    r'\\$\\{DB_PASSWORD\\}',                          # Using ${DB_PASSWORD}\n    r'DB_PASSWORD=\\$',                              # DB_PASSWORD=$OTHER_VAR\n    r'DB_PASSWORD=\"\"',                              # Empty assignment\n    r\"DB_PASSWORD=''\",                              # Empty assignment\n    r'getenv\\([\"\\']DB_PASSWORD',                   # PHP getenv\n    r'os\\.environ\\.get\\([\"\\']DB_PASSWORD',         # Python os.environ\n    r'get_db_password\\(\\)',                        # Calling getter function\n]\n\nERROR_MESSAGE = \"\"\"\nBLOCKIERT: Passwort-Exposition im Bash-Command erkannt!\n\nDu hast versucht, ein Passwort im Klartext in einem Bash-Befehl zu übergeben.\nDas exponiert das Passwort in:\n  - ps aux (für alle User sichtbar)\n  - \/proc\/*\/cmdline\n  - bash_history\n  - Konversations-Logs\n\nRICHTIG: Passwort aus Environment laden\n  Das Passwort ist bereits in \/etc\/environment gesetzt.\n  Einfach das Script ohne Passwort-Übergabe starten:\n\n  # RICHTIG:\n  .\/venv\/bin\/python script.py\n\n  # FALSCH:\n  DB_PASSWORD=\"xxx\" .\/venv\/bin\/python script.py\n\n  Das Script lädt das Passwort automatisch via:\n    from config import get_db_password\n    password = get_db_password()\n\nDokumentation: \/docs\/betrieb\/secrets\n\"\"\".strip()\n\n\ndef check_command(command: str) -> tuple[bool, str]:\n    \"\"\"\n    Prüft ob Befehl Passwort-Exposition enthält.\n\n    Returns:\n        (allowed, error_message)\n    \"\"\"\n    # Erst prüfen ob es ein erlaubtes Pattern ist\n    for pattern in ALLOWED_PATTERNS:\n        if re.search(pattern, command, re.IGNORECASE):\n            return True, \"\"\n\n    # Dann prüfen ob blockiertes Pattern vorhanden\n    for pattern in BLOCKED_PATTERNS:\n        if re.search(pattern, command, re.IGNORECASE):\n            return False, ERROR_MESSAGE\n\n    return True, \"\"\n\n\ndef main():\n    \"\"\"\n    Hook Entry Point (PreToolUse).\n\n    Input (stdin): JSON mit tool_name und tool_input\n    Output (stdout): JSON mit allow\/deny\n    \"\"\"\n    try:\n        # Lese Hook-Input von stdin\n        hook_input = json.loads(sys.stdin.read())\n\n        tool_name = hook_input.get(\"tool_name\", \"\")\n        tool_input = hook_input.get(\"tool_input\", {})\n\n        # Nur Bash-Tool prüfen\n        if tool_name != \"Bash\":\n            sys.exit(0)\n\n        # Befehl extrahieren\n        command = tool_input.get(\"command\", \"\")\n\n        # Prüfen ob Befehl blockiert werden soll\n        allowed, error_msg = check_command(command)\n\n        if not allowed:\n            # Output im Claude Code Format\n            output = {\n                \"hookSpecificOutput\": {\n                    \"hookEventName\": \"PreToolUse\",\n                    \"permissionDecision\": \"deny\",\n                    \"permissionDecisionReason\": error_msg\n                }\n            }\n            print(json.dumps(output))\n            sys.exit(1)\n\n        # Allowed: kein Output nötig\n        sys.exit(0)\n\n    except Exception as e:\n        # Bei Fehler: Fail-Open (durchlassen, aber warnen)\n        print(f\"Hook error (fail-open): {e}\", file=sys.stderr)\n        sys.exit(0)\n\n\nif __name__ == \"__main__\":\n    main()\n",
        "structuredPatch": [],
        "originalFile": null
    }
}
← Vorheriger Zur Liste Nächster →