cc-safe-setup 1.7.2 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/examples/auto-approve-python.sh +47 -0
- package/examples/block-database-wipe.sh +70 -0
- package/index.mjs +35 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ Installs 8 production-tested safety hooks in ~10 seconds. Zero dependencies. No
|
|
|
25
25
|
✗ Force-push rewriting shared branch history
|
|
26
26
|
✗ API keys committed to public repos via git add .
|
|
27
27
|
✗ Syntax errors cascading through 30+ files
|
|
28
|
+
✗ Laravel migrate:fresh wiping production database
|
|
28
29
|
✗ Sessions losing all context with no warning
|
|
29
30
|
|
|
30
31
|
Hooks to install:
|
|
@@ -136,11 +137,12 @@ Need custom hooks beyond the 8 built-in ones? See [`examples/`](examples/) for r
|
|
|
136
137
|
- **edit-guard.sh** — Block Edit/Write to protected files (defense-in-depth for [#37210](https://github.com/anthropics/claude-code/issues/37210))
|
|
137
138
|
- **auto-approve-build.sh** — Auto-approve npm/yarn/cargo/go/python build, test, and lint commands
|
|
138
139
|
- **auto-approve-docker.sh** — Auto-approve docker build, compose, ps, logs, and other safe commands
|
|
140
|
+
- **block-database-wipe.sh** — Block destructive database commands: Laravel `migrate:fresh`, Django `flush`, Rails `db:drop`, raw `DROP DATABASE` ([#37405](https://github.com/anthropics/claude-code/issues/37405) [#37439](https://github.com/anthropics/claude-code/issues/37439))
|
|
139
141
|
|
|
140
142
|
## Learn More
|
|
141
143
|
|
|
142
144
|
- [Official Hooks Reference](https://code.claude.com/docs/en/hooks) — Claude Code hooks documentation
|
|
143
|
-
- [Hooks Cookbook](https://github.com/yurukusa/claude-code-hooks/blob/main/COOKBOOK.md) —
|
|
145
|
+
- [Hooks Cookbook](https://github.com/yurukusa/claude-code-hooks/blob/main/COOKBOOK.md) — 12 ready-to-use recipes from real GitHub Issues
|
|
144
146
|
- [Japanese guide (Qiita)](https://qiita.com/yurukusa/items/a9714b33f5d974e8f1e8) — この記事の日本語解説
|
|
145
147
|
- [The incident that inspired this tool](https://github.com/anthropics/claude-code/issues/36339) — NTFS junction rm -rf
|
|
146
148
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# auto-approve-python.sh — Auto-approve Python development commands
|
|
3
|
+
#
|
|
4
|
+
# Solves: Permission prompts for pytest, mypy, ruff, black, isort
|
|
5
|
+
# that slow down autonomous Python development
|
|
6
|
+
#
|
|
7
|
+
# Usage: Add to settings.json as a PreToolUse hook
|
|
8
|
+
#
|
|
9
|
+
# {
|
|
10
|
+
# "hooks": {
|
|
11
|
+
# "PreToolUse": [{
|
|
12
|
+
# "matcher": "Bash",
|
|
13
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/auto-approve-python.sh" }]
|
|
14
|
+
# }]
|
|
15
|
+
# }
|
|
16
|
+
# }
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
20
|
+
|
|
21
|
+
[[ -z "$COMMAND" ]] && exit 0
|
|
22
|
+
|
|
23
|
+
# Python test runners
|
|
24
|
+
if echo "$COMMAND" | grep -qE '^\s*(pytest|python\s+-m\s+pytest|python\s+-m\s+unittest)(\s|$)'; then
|
|
25
|
+
jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"Python test auto-approved"}}'
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Linters and formatters
|
|
30
|
+
if echo "$COMMAND" | grep -qE '^\s*(ruff\s+(check|format)|black\s|isort\s|flake8\s|pylint\s|mypy\s|pyright\s)'; then
|
|
31
|
+
jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"Python lint/format auto-approved"}}'
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Package management (read-only)
|
|
36
|
+
if echo "$COMMAND" | grep -qE '^\s*(pip\s+list|pip\s+show|pip\s+freeze|pipdeptree)(\s|$)'; then
|
|
37
|
+
jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"pip read-only auto-approved"}}'
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Python syntax check
|
|
42
|
+
if echo "$COMMAND" | grep -qE '^\s*python3?\s+-m\s+py_compile\s'; then
|
|
43
|
+
jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"Python compile check auto-approved"}}'
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# block-database-wipe.sh — Block destructive database commands
|
|
3
|
+
#
|
|
4
|
+
# Prevents accidental database destruction from commands like:
|
|
5
|
+
# - Laravel: migrate:fresh, migrate:reset, db:wipe
|
|
6
|
+
# - Django: flush, sqlflush
|
|
7
|
+
# - Rails: db:drop, db:reset
|
|
8
|
+
# - Raw SQL: DROP DATABASE, TRUNCATE
|
|
9
|
+
# - PostgreSQL: dropdb
|
|
10
|
+
#
|
|
11
|
+
# Born from GitHub Issue #37405 (SQLite database wiped)
|
|
12
|
+
# and #37439 (Laravel migrate:fresh on production DB)
|
|
13
|
+
#
|
|
14
|
+
# Usage: Add to settings.json as a PreToolUse hook
|
|
15
|
+
#
|
|
16
|
+
# {
|
|
17
|
+
# "hooks": {
|
|
18
|
+
# "PreToolUse": [{
|
|
19
|
+
# "matcher": "Bash",
|
|
20
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/block-database-wipe.sh" }]
|
|
21
|
+
# }]
|
|
22
|
+
# }
|
|
23
|
+
# }
|
|
24
|
+
|
|
25
|
+
INPUT=$(cat)
|
|
26
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
27
|
+
|
|
28
|
+
[[ -z "$COMMAND" ]] && exit 0
|
|
29
|
+
|
|
30
|
+
# Laravel destructive commands
|
|
31
|
+
if echo "$COMMAND" | grep -qiE 'artisan\s+(migrate:fresh|migrate:reset|db:wipe|db:seed\s+--force)'; then
|
|
32
|
+
echo "BLOCKED: Destructive Laravel database command" >&2
|
|
33
|
+
echo "Command: $COMMAND" >&2
|
|
34
|
+
exit 2
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Laravel --env flag without corresponding .env file
|
|
38
|
+
if echo "$COMMAND" | grep -qE 'artisan.*--env='; then
|
|
39
|
+
ENV_NAME=$(echo "$COMMAND" | grep -oP '(?<=--env=)\w+')
|
|
40
|
+
if [ -n "$ENV_NAME" ] && [ ! -f ".env.$ENV_NAME" ]; then
|
|
41
|
+
echo "BLOCKED: .env.$ENV_NAME does not exist. Command would fall back to .env (possibly production)" >&2
|
|
42
|
+
exit 2
|
|
43
|
+
fi
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Django destructive commands
|
|
47
|
+
if echo "$COMMAND" | grep -qiE 'manage\.py\s+(flush|sqlflush)'; then
|
|
48
|
+
echo "BLOCKED: Destructive Django database command" >&2
|
|
49
|
+
exit 2
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Rails destructive commands
|
|
53
|
+
if echo "$COMMAND" | grep -qiE 'rake\s+db:(drop|reset)|rails\s+db:(drop|reset)'; then
|
|
54
|
+
echo "BLOCKED: Destructive Rails database command" >&2
|
|
55
|
+
exit 2
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Raw SQL destructive commands
|
|
59
|
+
if echo "$COMMAND" | grep -qiE 'DROP\s+(DATABASE|TABLE|SCHEMA)|TRUNCATE\s+TABLE|DELETE\s+FROM\s+\w+\s*;?\s*$'; then
|
|
60
|
+
echo "BLOCKED: Destructive SQL command" >&2
|
|
61
|
+
exit 2
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# PostgreSQL CLI
|
|
65
|
+
if echo "$COMMAND" | grep -qE '^\s*dropdb\s'; then
|
|
66
|
+
echo "BLOCKED: dropdb command" >&2
|
|
67
|
+
exit 2
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
exit 0
|
package/index.mjs
CHANGED
|
@@ -65,6 +65,7 @@ const HOOKS = {
|
|
|
65
65
|
const HELP = process.argv.includes('--help') || process.argv.includes('-h');
|
|
66
66
|
const STATUS = process.argv.includes('--status') || process.argv.includes('-s');
|
|
67
67
|
const VERIFY = process.argv.includes('--verify') || process.argv.includes('-v');
|
|
68
|
+
const EXAMPLES = process.argv.includes('--examples') || process.argv.includes('-e');
|
|
68
69
|
|
|
69
70
|
if (HELP) {
|
|
70
71
|
console.log(`
|
|
@@ -76,6 +77,7 @@ if (HELP) {
|
|
|
76
77
|
npx cc-safe-setup --verify Test each hook with sample inputs
|
|
77
78
|
npx cc-safe-setup --dry-run Preview without installing
|
|
78
79
|
npx cc-safe-setup --uninstall Remove all installed hooks
|
|
80
|
+
npx cc-safe-setup --examples List available example hooks
|
|
79
81
|
npx cc-safe-setup --help Show this help
|
|
80
82
|
|
|
81
83
|
Hooks installed:
|
|
@@ -248,10 +250,43 @@ async function verify() {
|
|
|
248
250
|
console.log();
|
|
249
251
|
}
|
|
250
252
|
|
|
253
|
+
function examples() {
|
|
254
|
+
const examplesDir = join(__dirname, 'examples');
|
|
255
|
+
const EXAMPLE_DESCRIPTIONS = {
|
|
256
|
+
'auto-approve-build.sh': 'Auto-approve npm/yarn/cargo/go build, test, lint commands',
|
|
257
|
+
'auto-approve-docker.sh': 'Auto-approve docker build, compose, ps, logs commands',
|
|
258
|
+
'auto-approve-git-read.sh': 'Auto-approve git status/log/diff even with -C flags',
|
|
259
|
+
'auto-approve-ssh.sh': 'Auto-approve safe SSH commands (uptime, whoami, etc.)',
|
|
260
|
+
'block-database-wipe.sh': 'Block destructive DB commands (migrate:fresh, DROP DATABASE)',
|
|
261
|
+
'edit-guard.sh': 'Block Edit/Write to protected files (.env, credentials)',
|
|
262
|
+
'enforce-tests.sh': 'Warn when source files change without test files',
|
|
263
|
+
'notify-waiting.sh': 'Desktop notification when Claude waits for input',
|
|
264
|
+
'auto-approve-python.sh': 'Auto-approve pytest, mypy, ruff, black, isort commands',
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
console.log();
|
|
268
|
+
console.log(c.bold + ' cc-safe-setup --examples' + c.reset);
|
|
269
|
+
console.log(c.dim + ' Custom hooks beyond the 8 built-in ones' + c.reset);
|
|
270
|
+
console.log();
|
|
271
|
+
|
|
272
|
+
for (const [file, desc] of Object.entries(EXAMPLE_DESCRIPTIONS)) {
|
|
273
|
+
const fullPath = join(examplesDir, file);
|
|
274
|
+
const exists = existsSync(fullPath);
|
|
275
|
+
console.log(' ' + c.green + '*' + c.reset + ' ' + c.bold + file + c.reset);
|
|
276
|
+
console.log(' ' + c.dim + desc + c.reset);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
console.log();
|
|
280
|
+
console.log(c.dim + ' Copy any example to ~/.claude/hooks/ and add to settings.json.' + c.reset);
|
|
281
|
+
console.log(c.dim + ' Source: ' + c.blue + 'https://github.com/yurukusa/cc-safe-setup/tree/main/examples' + c.reset);
|
|
282
|
+
console.log();
|
|
283
|
+
}
|
|
284
|
+
|
|
251
285
|
async function main() {
|
|
252
286
|
if (UNINSTALL) return uninstall();
|
|
253
287
|
if (VERIFY) return verify();
|
|
254
288
|
if (STATUS) return status();
|
|
289
|
+
if (EXAMPLES) return examples();
|
|
255
290
|
|
|
256
291
|
console.log();
|
|
257
292
|
console.log(c.bold + ' cc-safe-setup' + c.reset);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "One command to make Claude Code safe for autonomous operation. 8 hooks: destructive blocker, branch guard, force-push protection, secret leak prevention, syntax checks, and more.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"bin": {
|