mirror of
https://github.com/trailofbits/claude-code-config
synced 2026-06-21 14:11:43 +00:00
58b6f73e2a
Deny rules previously only blocked `rm -rf` and `rm -fr` (lowercase, combined flags only). All other flag forms bypassed both security layers silently. Expand deny rules to cover: - All case combinations of combined short flags: -Rf, -rF, -RF, -fR, -Fr, -FR - All orderings of separated short flags: -r -f, -r -F, -R -f, -R -F and their reverses (-f -r, -f -R, -F -r, -F -R) - GNU long-form flags: --recursive, --recursive --force, --force --recursive - Pipe-to-shell via sh and zsh: * | sh, * | zsh - Process substitution: bash <(curl *) - Spaced pipe variant for existing rules: curl *| bash*, wget *| bash* Harden PreToolUse hook regex: - Old regex required combined lowercase flags only (-rf / -fr pattern) - New regex uses three chained case-insensitive grep checks: 1. Confirm rm is the actual command (not a substring of another word or arg) 2. Detect any recursive flag (-r, -R, -[flags]r, --recursive) 3. Detect any force flag (-f, -F, -[flags]f, --force) - Handles commands chained with ; && || | - Verified against 25 test cases (19 must-block, 6 must-allow)
93 lines
2.9 KiB
JSON
93 lines
2.9 KiB
JSON
{
|
|
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
"cleanupPeriodDays": 365,
|
|
"env": {
|
|
"DISABLE_TELEMETRY": "1",
|
|
"DISABLE_ERROR_REPORTING": "1",
|
|
"CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY": "1",
|
|
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
|
|
},
|
|
"enableAllProjectMcpServers": false,
|
|
"alwaysThinkingEnabled": true,
|
|
"permissions": {
|
|
"deny": [
|
|
"Bash(rm -rf *)",
|
|
"Bash(rm -fr *)",
|
|
"Bash(rm -Rf *)",
|
|
"Bash(rm -rF *)",
|
|
"Bash(rm -RF *)",
|
|
"Bash(rm -fR *)",
|
|
"Bash(rm -Fr *)",
|
|
"Bash(rm -FR *)",
|
|
"Bash(rm -r -f *)",
|
|
"Bash(rm -r -F *)",
|
|
"Bash(rm -R -f *)",
|
|
"Bash(rm -R -F *)",
|
|
"Bash(rm -f -r *)",
|
|
"Bash(rm -f -R *)",
|
|
"Bash(rm -F -r *)",
|
|
"Bash(rm -F -R *)",
|
|
"Bash(rm --recursive *)",
|
|
"Bash(rm --recursive --force *)",
|
|
"Bash(rm --force --recursive *)",
|
|
"Bash(sudo *)",
|
|
"Bash(mkfs *)",
|
|
"Bash(dd *)",
|
|
"Bash(curl *|bash*)",
|
|
"Bash(curl *| bash*)",
|
|
"Bash(wget *|bash*)",
|
|
"Bash(wget *| bash*)",
|
|
"Bash(* | sh)",
|
|
"Bash(* | zsh)",
|
|
"Bash(bash <(curl *))",
|
|
"Bash(git push --force*)",
|
|
"Bash(git push *--force*)",
|
|
"Bash(git reset --hard*)",
|
|
|
|
"Edit(~/.bashrc)",
|
|
"Edit(~/.zshrc)",
|
|
"Edit(~/.ssh/**)",
|
|
|
|
"Read(~/.ssh/**)",
|
|
"Read(~/.gnupg/**)",
|
|
"Read(~/.aws/**)",
|
|
"Read(~/.azure/**)",
|
|
"Read(~/.config/gh/**)",
|
|
"Read(~/.git-credentials)",
|
|
"Read(~/.docker/config.json)",
|
|
"Read(~/.kube/**)",
|
|
"Read(~/.npmrc)",
|
|
"Read(~/.npm/**)",
|
|
"Read(~/.pypirc)",
|
|
"Read(~/.gem/credentials)",
|
|
"Read(~/Library/Keychains/**)",
|
|
"Read(~/Library/Application Support/**/metamask*/**)",
|
|
"Read(~/Library/Application Support/**/electrum*/**)",
|
|
"Read(~/Library/Application Support/**/exodus*/**)",
|
|
"Read(~/Library/Application Support/**/phantom*/**)",
|
|
"Read(~/Library/Application Support/**/solflare*/**)"
|
|
]
|
|
},
|
|
"hooks": {
|
|
"PreToolUse": [
|
|
{
|
|
"matcher": "Bash",
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "CMD=$(jq -r '.tool_input.command'); if echo \"$CMD\" | grep -qiE '(^|;[[:space:]]*|&&[[:space:]]*|[|][|][[:space:]]*|[|][[:space:]]*)rm[[:space:]]' && echo \"$CMD\" | grep -qiE '(^|[[:space:]])-[a-zA-Z]*[rR]|--recursive' && echo \"$CMD\" | grep -qiE '(^|[[:space:]])-[a-zA-Z]*[fF]|--force'; then echo 'BLOCKED: Use trash instead of rm -rf' >&2; exit 2; fi"
|
|
},
|
|
{
|
|
"type": "command",
|
|
"command": "CMD=$(jq -r '.tool_input.command'); if echo \"$CMD\" | grep -qiE 'git[[:space:]]+push.*(main|master)'; then echo 'BLOCKED: Use feature branches, not direct push to main' >&2; exit 2; fi"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"statusLine": {
|
|
"type": "command",
|
|
"command": "~/.claude/statusline.sh"
|
|
}
|
|
}
|