-
Notifications
You must be signed in to change notification settings - Fork 12
feat: add 3 example hooks (destructive, secret, branch guard) #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #!/usr/bin/env python3 | ||
| """Branch protection using cchooks SDK. | ||
|
|
||
| Blocks push to main/master and force-push on all branches. | ||
| Install: Add to PreToolUse hooks matching "Bash" in settings.json. | ||
| """ | ||
| import re | ||
| from cchooks import create_context | ||
| from cchooks.contexts import PreToolUseContext | ||
|
|
||
| ctx = create_context() | ||
| if isinstance(ctx, PreToolUseContext): | ||
| cmd = ctx.tool_input.get("command", "") | ||
|
|
||
| if re.search(r'\bgit\s+push\b.*\b(main|master)\b', cmd, re.I): | ||
| ctx.output.block(reason="Direct push to main/master branch") | ||
| elif re.search(r'\bgit\s+push\s+.*--force\b', cmd, re.I): | ||
| ctx.output.block(reason="Force push overwrites remote history") | ||
| elif re.search(r'\bgit\s+push\s+.*-f\b', cmd, re.I): | ||
| ctx.output.block(reason="Force push (-f shorthand)") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #!/usr/bin/env python3 | ||
| """Destructive command guard using cchooks SDK. | ||
|
|
||
| Blocks rm -rf /, git reset --hard, and similar destructive commands. | ||
| Install: Add to PreToolUse hooks matching "Bash" in settings.json. | ||
|
|
||
| Born from: https://github.com/anthropics/claude-code/issues/36339 | ||
| (User lost entire C:\Users directory to rm -rf via NTFS junctions) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The docstring includes Useful? React with 👍 / 👎. |
||
| """ | ||
| import re | ||
| from cchooks import create_context | ||
| from cchooks.contexts import PreToolUseContext | ||
|
|
||
| DANGEROUS = [ | ||
| re.compile(r'\brm\s+.*-rf\s+(/|~/?\s*$|\.\./)', re.I), | ||
| re.compile(r'\bgit\s+reset\s+--hard', re.I), | ||
| re.compile(r'\bgit\s+clean\s+-[a-zA-Z]*f', re.I), | ||
| re.compile(r'\bchmod\s+(-R\s+)?777\s+/', re.I), | ||
| re.compile(r'\bfind\s+/\s+-delete', re.I), | ||
| re.compile(r'Remove-Item.*-Recurse.*-Force', re.I), | ||
| re.compile(r'--no-preserve-root', re.I), | ||
| ] | ||
|
|
||
| ctx = create_context() | ||
| if isinstance(ctx, PreToolUseContext): | ||
| cmd = ctx.tool_input.get("command", "") | ||
| if cmd and not cmd.lstrip().startswith(("echo ", "printf ")): | ||
| for pattern in DANGEROUS: | ||
| if pattern.search(cmd): | ||
| ctx.output.block(reason=f"Destructive command: {cmd[:80]}") | ||
| break | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #!/usr/bin/env python3 | ||
| """Secret leak prevention using cchooks SDK. | ||
|
|
||
| Blocks git add .env, credential files, and git add . when .env exists. | ||
| Install: Add to PreToolUse hooks matching "Bash" in settings.json. | ||
|
|
||
| Born from: https://github.com/anthropics/claude-code/issues/16561 | ||
| (API keys committed via git add .) | ||
| """ | ||
| import os | ||
| import re | ||
| from cchooks import create_context | ||
| from cchooks.contexts import PreToolUseContext | ||
|
|
||
| ctx = create_context() | ||
| if isinstance(ctx, PreToolUseContext): | ||
| cmd = ctx.tool_input.get("command", "") | ||
|
|
||
| # Block git add .env* | ||
| if re.search(r'\bgit\s+add\s+.*\.env\b', cmd, re.I): | ||
| ctx.output.block(reason="Adding .env file to git exposes secrets") | ||
|
|
||
| # Block git add of credential files | ||
| elif re.search(r'\bgit\s+add\s+.*(\.pem|\.key|id_rsa|credentials)', cmd, re.I): | ||
| ctx.output.block(reason="Adding credential file to git") | ||
|
|
||
| # Warn on git add . / git add -A (may include .env) | ||
| elif re.search(r'\bgit\s+add\s+(-A|--all|\.)\s*$', cmd): | ||
| if os.path.exists('.env') or os.path.exists('.env.local'): | ||
| ctx.output.block(reason="git add all with .env present — use specific files") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
output.blockcallsPreToolUseOutputhas noblock()method, soctx.output.block(...)raisesAttributeErrorwhenever a rule matches. That turns an intended deny into a hook crash (exit code 1/non-blocking), so commands likegit push origin mainare not actually denied. The same pattern appears across the three new examples and should use a supported PreToolUse API such asdeny(...)orexit_block(...).Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix needed.