Skip to content
Back to skills

607 Best Practices F04ebc43

CSecurity

Security, performance, reliability, and maintainability guidelines for Claude Code hooks.

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 1, 2026
code-qualitypythonrustshellbashawstestingdebugginggitapisecurity

Works with

  • claude code
  • api

Security analysis

C64/100
  • criticalAccesses sensitive system or user directories
  • highPerforms destructive filesystem operations
  • criticalReads or references SSH private keys
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned June 1, 2026

npx -y skills add tools-only/X-Skills --skill 607-best-practices_f04ebc43 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 607 Best Practices F04ebc43?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 607 Best Practices F04ebc43
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-607-best-practices-f04ebc43/badge)](https://www.skillsdirectory.com/skills/tools-only-607-best-practices-f04ebc43)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Hook Best Practices

Security, performance, reliability, and maintainability guidelines for Claude Code hooks.

## Security Best Practices

### Input Validation

Always validate and sanitize inputs:

```python
# DON'T: Trust input blindly
command = input_data.get("tool_input", {}).get("command")
os.system(command)  # DANGEROUS!

# DO: Validate and sanitize
command = input_data.get("tool_input", {}).get("command", "")
if not command or not validate_command(command):
    print("Invalid command", file=sys.stderr)
    sys.exit(2)
```

### Shell Quoting

Always quote shell variables:

```bash
# DON'T: Unquoted variables
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
cat $file_path  # Shell injection risk!

# DO: Quote variables
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
cat "$file_path"  # Safe
```

### Path Traversal Prevention

Check for path traversal attacks:

```python
def is_safe_path(base_dir, file_path):
    """Prevent ../../../etc/passwd attacks"""
    try:
        base = Path(base_dir).resolve()
        target = Path(file_path).resolve()
        return target.is_relative_to(base)
    except (ValueError, OSError):
        return False

# Use it
if not is_safe_path(project_dir, file_path):
    print("Path traversal detected", file=sys.stderr)
    sys.exit(2)
```

### Sensitive File Protection

Skip sensitive files:

```python
SENSITIVE_PATHS = (
    ".env", ".env.local", ".env.production",
    ".git/config", ".ssh/", "id_rsa",
    "credentials.json", "secrets.yaml",
    ".aws/credentials"
)

def is_sensitive(file_path):
    return any(p in file_path for p in SENSITIVE_PATHS)

if is_sensitive(file_path):
    print("Sensitive file access blocked", file=sys.stderr)
    sys.exit(2)
```

### Secret Detection

Scan for hardcoded secrets:

```python
SECRET_PATTERNS = [
    r'ghp_[a-zA-Z0-9]{36}',  # GitHub token
    r'sk_live_[a-zA-Z0-9]{24,}',  # Stripe key
    r'xoxb-[a-zA-Z0-9-]+',  # Slack token
    r'AKIA[0-9A-Z]{16}',  # AWS key
    r'(?i)password\s*[:=]\s*["\']?[\w!@#$%^&*]{8,}',
]

def contains_secrets(text):
    return any(re.search(p, text) for p in SECRET_PATTERNS)
```

### Use Absolute Paths

Always use absolute paths for scripts:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/script.py"
          }
        ]
      }
    ]
  }
}
```

## Performance Best Practices

### Keep Hooks Fast

Hooks should complete quickly (<1s ideal, <5s maximum):

```python
# DON'T: Slow operations
result = requests.get("https://api.example.com/slow")  # May take seconds

# DO: Quick checks, background for slow operations
if should_run_slow_check():
    subprocess.Popen(["./slow-check.sh", file_path])  # Background
sys.exit(0)
```

### Exit Early

Skip unnecessary processing:

```python
# DON'T: Process everything
input_data = json.load(sys.stdin)
tool_name = input_data.get("tool_name")
# ... lots of processing ...
if tool_name != "Bash":
    sys.exit(0)

# DO: Exit early
input_data = json.load(sys.stdin)
if input_data.get("tool_name") != "Bash":
    sys.exit(0)
# ... relevant processing only ...
```

### Cache Expensive Operations

Cache when possible:

```python
import functools

@functools.lru_cache(maxsize=128)
def get_project_config():
    """Cached config loading"""
    with open(".project-config.json") as f:
        return json.load(f)
```

### Set Appropriate Timeouts

Configure timeouts based on operation:

```json
{
  "hooks": [
    {
      "type": "command",
      "command": "quick-check.sh",
      "timeout": 5
    },
    {
      "type": "command",
      "command": "slow-formatter.sh",
      "timeout": 30
    }
  ]
}
```

### Minimize Tool Dependencies

Only import what's needed:

```python
# DON'T: Import everything
import json, sys, os, re, subprocess, pathlib, datetime, requests

# DO: Import only what's used
import json
import sys
```

## Reliability Best Practices

### Error Handling

Handle errors gracefully:

```python
try:
    result = subprocess.run(
        ["formatter", file_path],
        capture_output=True,
        text=True,
        timeout=30,
        check=True
    )
except FileNotFoundError:
    print("Formatter not installed", file=sys.stderr)
    sys.exit(1)
except subprocess.TimeoutExpired:
    print("Formatting timed out", file=sys.stderr)
    sys.exit(1)
except subprocess.CalledProcessError as e:
    print(f"Formatting failed: {e.stderr}", file=sys.stderr)
    sys.exit(1)
except Exception as e:
    print(f"Unexpected error: {e}", file=sys.stderr)
    sys.exit(1)
```

### Validate JSON Schema

Check for required fields:

```python
def validate_input(data):
    """Validate hook input has required fields"""
    required = ["session_id", "hook_event_name"]
    for field in required:
        if field not in data:
            print(f"Missing required field: {field}", file=sys.stderr)
            sys.exit(1)

input_data = json.load(sys.stdin)
validate_input(input_data)
```

### Handle Missing Dependencies

Check for required tools:

```python
import shutil

def check_dependencies():
    """Ensure required tools are available"""
    required_tools = ["black", "pylint"]
    missing = [t for t in required_tools if not shutil.which(t)]

    if missing:
        print(f"Missing tools: {', '.join(missing)}", file=sys.stderr)
        print("Install with: pip install black pylint", file=sys.stderr)
        sys.exit(1)
```

### Provide Clear Error Messages

Make errors actionable:

```python
# DON'T: Vague error
print("Error", file=sys.stderr)

# DO: Specific, actionable error
print("""
❌ Linting failed for {file_path}

Issues found:
{issues}

Fix with: pylint {file_path}
Or disable: # pylint: disable=rule-name
""", file=sys.stderr)
```

## Maintainability Best Practices

### Document Hook Purpose

Include clear documentation:

```python
#!/usr/bin/env python3
"""
Validates Bash commands before execution.

Blocks:
- rm -rf commands targeting system directories
- sudo/su privilege escalation
- Direct device access

Exit codes:
- 0: Command allowed
- 2: Command blocked
- 1: Validation error
"""
```

### Use Descriptive Names

Name hooks clearly:

```bash
# DON'T: Generic names
hook1.py
check.sh
validator.py

# DO: Descriptive names
validate-bash-security.py
auto-format-python.py
check-git-clean.sh
```

### Version Control

Track hooks in git:

```bash
# Add hooks to git
git add .claude/hooks/
git commit -m "Add bash validation hook"

# Add to .gitignore if needed (for local hooks)
echo ".claude/settings.local.json" >> .gitignore
```

### README Documentation

Document hooks in project README:

```markdown
## Claude Code Hooks

This project uses the following hooks:

### PreToolUse Hooks
- **validate-bash.py**: Blocks dangerous bash commands
- **check-paths.py**: Prevents path traversal

### PostToolUse Hooks
- **auto-format.py**: Formats Python files with black

### Setup
```bash
chmod +x .claude/hooks/*.py
```
```

### Configuration Comments

Add comments to settings.json:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/validate-bash.py"
            // Blocks rm -rf, sudo, and other dangerous commands
          }
        ]
      }
    ]
  }
}
```

## Configuration Best Practices

### Use Project-Level Hooks for Team

Share hooks with team:

```bash
# .claude/settings.json (committed to git)
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/validate.py"
          }
        ]
      }
    ]
  }
}
```

### Use User-Level Hooks for Personal

Personal preferences:

```bash
# ~/.claude/settings.json (not in git)
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/my-personal-setup.sh"
          }
        ]
      }
    ]
  }
}
```

### Use Local Hooks for Testing

Test hooks without affecting team:

```bash
# .claude/settings.local.json (gitignored)
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PROJECT_DIR}\"/.claude/hooks/test-hook.py"
          }
        ]
      }
    ]
  }
}
```

### Organize Hooks by Purpose

```
.claude/
├── hooks/
│   ├── security/
│   │   ├── validate-bash.py
│   │   └── check-secrets.py
│   ├── formatting/
│   │   ├── auto-format.py
│   │   └── run-linters.py
│   └── git/
│       ├── check-clean.py
│       └── load-context.py
└── settings.json
```

## Stop Hook Safety

Prevent infinite loops in Stop hooks:

```python
# ALWAYS check stop_hook_active
if input_data.get("stop_hook_active"):
    sys.exit(0)  # Allow stopping if already in stop hook

# Your validation logic
has_issues = check_for_issues()

if has_issues:
    output = {"decision": "block", "reason": "Fix issues first"}
    print(json.dumps(output))
    sys.exit(0)
```

## Progressive Enhancement

Start simple, add complexity as needed:

**Phase 1: Basic validation**
```python
if "rm -rf /" in command:
    sys.exit(2)
```

**Phase 2: Pattern matching**
```python
if re.search(r'\brm\s+-rf\s+/', command):
    sys.exit(2)
```

**Phase 3: Comprehensive rules**
```python
for pattern, message in RULES:
    if re.search(pattern, command):
        print(message, file=sys.stderr)
        sys.exit(2)
```

**Phase 4: JSON output with context**
```python
output = {
    "decision": "block",
    "reason": message,
    "hookSpecificOutput": {...}
}
print(json.dumps(output))
```

## Security Checklist

Before deploying hooks to production:

- [ ] All user inputs are validated and sanitized
- [ ] Shell variables are properly quoted
- [ ] Path traversal attacks are prevented
- [ ] Sensitive file access is blocked or logged
- [ ] No hardcoded secrets in hook scripts
- [ ] Absolute paths used for all script references
- [ ] Stop hooks check `stop_hook_active` flag
- [ ] Error messages don't leak sensitive information
- [ ] Hooks run with minimum required permissions
- [ ] External commands use full paths or validation

## Performance Checklist

Before deploying hooks to production:

- [ ] Hooks complete in <1s for common operations
- [ ] Maximum timeout set appropriately (5-30s)
- [ ] Early exit for non-matching tools
- [ ] Expensive operations are cached
- [ ] Slow operations run in background
- [ ] Minimal dependencies imported
- [ ] No blocking network calls in critical path
- [ ] File I/O is minimized
- [ ] Regex patterns are optimized
- [ ] Hook doesn't process unnecessary data

## Reliability Checklist

Before deploying hooks to production:

- [ ] All exceptions are caught and handled
- [ ] Missing dependencies are checked at startup
- [ ] Required fields in input JSON are validated
- [ ] Timeout errors are handled gracefully
- [ ] Error messages are clear and actionable
- [ ] Hooks tested with edge cases
- [ ] Hooks tested with malformed input
- [ ] Fallback behavior defined for failures
- [ ] Logs capture enough detail for debugging
- [ ] Hooks fail safely (allow operation on error)

Files in this skill

  • README.md1015 B
  • skill.md11.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.