intermediate· via claude-howto

Hook: Scanner de secrets en código

PostToolUse hook que detecta passwords y API keys hardcodeadas en archivos escritos.

hooksecurityposttoolusesecretscredentials
hook
# Archivo: .claude/hooks/security-scanner.py
# Evento: PostToolUse (matcher: Write|Edit)

#!/usr/bin/env python3
import json
import sys
import re

SECRET_PATTERNS = [
    (r'password\s*=\s*['"][^'"]+['"]', "Password hardcodeada detectada"),
    (r'api[_-]?key\s*=\s*['"][^'"]{8,}['"]', "API key hardcodeada detectada"),
    (r'secret\s*=\s*['"][^'"]{8,}['"]', "Secret hardcodeado detectado"),
    (r'bearer\s+[a-zA-Z0-9_\-\.]+', "Bearer token en código"),
    (r'sk-[a-zA-Z0-9]{32,}', "Posible OpenAI/Anthropic API key"),
]

def main():
    data = json.load(sys.stdin)
    if data.get("tool_name") not in ["Write", "Edit"]:
        sys.exit(0)

    tool_input = data.get("tool_input", {})
    content = tool_input.get("content", "") or tool_input.get("new_string", "")
    file_path = tool_input.get("file_path", "")

    # Ignorar archivos de entorno y test fixtures
    if any(x in file_path for x in [".env", "fixture", "test/mock"]):
        sys.exit(0)

    warnings = []
    for pattern, message in SECRET_PATTERNS:
        if re.search(pattern, content, re.IGNORECASE):
            warnings.append(message)

    if warnings:
        output = {
            "hookSpecificOutput": {
                "hookEventName": "PostToolUse",
                "additionalContext": f"⚠️  {file_path}: " + " | ".join(warnings) +
                    " — Usa process.env en su lugar."
            }
        }
        print(json.dumps(output))

    sys.exit(0)

if __name__ == "__main__":
    main()