Skip to content

Instantly share code, notes, and snippets.

@danklammer
Forked from sgasser/README.md
Last active February 5, 2026 22:34
Show Gist options
  • Select an option

  • Save danklammer/246e568785f2b0b48a5cadc45369cdc9 to your computer and use it in GitHub Desktop.

Select an option

Save danklammer/246e568785f2b0b48a5cadc45369cdc9 to your computer and use it in GitHub Desktop.
Claude Code security hook - blocks dangerous commands, credential files, and bypass attempts
# Clauded - "I also like to live dangerously"
alias clauded="claude --dangerously-skip-permissions"

Claude Code Security Setup

Two-layer protection for sensitive files and destructive commands.

Setup

  1. Copy settings.json content to ~/.claude/settings.json
  2. Save security-validator.py to ~/.claude/hooks/security-validator.py
  3. Make executable: chmod +x ~/.claude/hooks/security-validator.py

How it works

permissions.deny - Blocks Claude from reading sensitive files:

  • .env files
  • SSH keys, PEM certificates
  • Cloud credentials (AWS, Azure, GCloud)
  • Kubernetes configs, Docker configs
  • Database passwords, shell history
  • And more...

Hook (security-validator.py) - Blocks destructive Bash commands:

  • rm -rf patterns
  • Recursive deletion at root/home level

Why both?

  • permissions.deny is native to Claude Code and blocks the Read tool
  • The hook provides extra protection for --dangerously-skip-permissions mode
#!/usr/bin/env python3
"""
Security validator hook for Claude Code.
Blocks destructive commands like 'rm -rf'.
For file access blocking, use permissions.deny in settings.json instead.
"""
import json
import sys
import re
def check_bash_command(command: str) -> tuple[bool, str]:
"""
Validate bash commands for dangerous patterns.
Returns: (is_allowed, error_message)
"""
# Block rm -rf / rm -r -f patterns
if re.search(r'\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)\b', command):
return False, "rm -rf is blocked. Use explicit rm commands with specific file paths."
# Block rm -r at root/home level
if re.search(r'\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+[/~]', command):
return False, "Recursive deletion at root/home level is blocked."
return True, ""
def main():
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
sys.exit(1)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# Only validate Bash commands
if tool_name != "Bash":
sys.exit(0)
command = tool_input.get("command", "")
if not command:
sys.exit(0)
is_allowed, error_msg = check_bash_command(command)
if not is_allowed:
# Exit code 2 = block with error message
print(f"SECURITY BLOCK: {error_msg}", file=sys.stderr)
sys.exit(2)
sys.exit(0)
if __name__ == "__main__":
main()
{
"permissions": {
"allow": [
"Write(*)",
"Update(*)",
"Bash(ls:*)",
"Bash(pwd:*)",
"Bash(cd:*)",
"Bash(echo:*)",
"Bash(printf:*)",
"Bash(cat:*)",
"Bash(less:*)",
"Bash(more:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(wc:*)",
"Bash(stat:*)",
"Bash(file:*)",
"Bash(mkdir:*)",
"Bash(touch:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Bash(chmod:*)",
"Bash(find:*)",
"Bash(grep:*)",
"Bash(rg:*)",
"Bash(sed:*)",
"Bash(awk:*)",
"Bash(xargs:*)",
"Bash(diff:*)",
"Bash(git:*)",
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(make:*)",
"Bash(go:*)",
"Bash(python:*)",
"Bash(python3:*)",
"Bash(poetry:*)",
"Bash(node:*)",
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(pnpm:*)",
"Bash(yarn:*)",
"Bash(sqlite3:*)",
"Bash(lsof:*)"
],
"deny": [
"Bash(rm:*)",
"Bash(rmdir:*)",
"Bash(dd:*)",
"Bash(mkfs:*)",
"Bash(sudo:*)",
"Bash(chown:*)",
"Bash(chattr:*)",
"Bash(mount:*)",
"Bash(umount:*)",
"Bash(shutdown:*)",
"Bash(reboot:*)",
"Read(**/.env)",
"Read(**/.env.*)",
"Read(**/.ssh/id_*)",
"Read(**/id_rsa)",
"Read(**/id_ed25519)",
"Read(**/*.pem)",
"Read(**/*.p12)",
"Read(**/*.pfx)",
"Read(**/*.ppk)",
"Read(**/*.gpg)",
"Read(**/*.pgp)",
"Read(**/*.asc)",
"Read(**/.aws/credentials)",
"Read(**/.azure/**)",
"Read(**/.config/gcloud/**)",
"Read(**/.kube/config)",
"Read(**/*vault_pass*)",
"Read(**/secrets.yml)",
"Read(**/secrets.yaml)",
"Read(**/secrets.json)",
"Read(**/credentials.json)",
"Read(**/.htpasswd)",
"Read(**/.netrc)",
"Read(**/.npmrc)",
"Read(**/.pypirc)",
"Read(**/application.properties)",
"Read(**/appsettings.json)",
"Read(**/*.tfstate)",
"Read(**/.git/config)",
"Read(**/.git-credentials)",
"Read(**/.bash_history)",
"Read(**/.zsh_history)",
"Read(**/.pgpass)",
"Read(**/.my.cnf)",
"Read(**/.docker/config.json)",
"Read(**/*.jks)",
"Read(**/*.keystore)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/security-validator.py"
}
]
}
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment