cc-safe-setup 1.8.2 → 1.8.4

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 CHANGED
@@ -140,11 +140,14 @@ Need custom hooks beyond the 8 built-in ones? See [`examples/`](examples/) for r
140
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))
141
141
  - **auto-approve-python.sh** — Auto-approve pytest, mypy, ruff, black, isort, flake8, pylint commands
142
142
  - **auto-snapshot.sh** — Auto-save file snapshots before edits for rollback protection ([#37386](https://github.com/anthropics/claude-code/issues/37386) [#37457](https://github.com/anthropics/claude-code/issues/37457))
143
+ - **allowlist.sh** — Block everything not explicitly approved — inverse permission model ([#37471](https://github.com/anthropics/claude-code/issues/37471))
144
+ - **protect-dotfiles.sh** — Block modifications to `~/.bashrc`, `~/.aws/`, `~/.ssh/` and chezmoi without diff ([#37478](https://github.com/anthropics/claude-code/issues/37478))
145
+ - **scope-guard.sh** — Block file operations outside project directory — absolute paths, home, parent escapes ([#36233](https://github.com/anthropics/claude-code/issues/36233))
143
146
 
144
147
  ## Learn More
145
148
 
146
149
  - [Official Hooks Reference](https://code.claude.com/docs/en/hooks) — Claude Code hooks documentation
147
- - [Hooks Cookbook](https://github.com/yurukusa/claude-code-hooks/blob/main/COOKBOOK.md) — 12 ready-to-use recipes from real GitHub Issues
150
+ - [Hooks Cookbook](https://github.com/yurukusa/claude-code-hooks/blob/main/COOKBOOK.md) — 13 ready-to-use recipes from real GitHub Issues
148
151
  - [Japanese guide (Qiita)](https://qiita.com/yurukusa/items/a9714b33f5d974e8f1e8) — この記事の日本語解説
149
152
  - [The incident that inspired this tool](https://github.com/anthropics/claude-code/issues/36339) — NTFS junction rm -rf
150
153
 
@@ -0,0 +1,38 @@
1
+ # Example Hooks
2
+
3
+ Custom hooks beyond the 8 built-in ones. Copy any file to `~/.claude/hooks/` and add to `settings.json`.
4
+
5
+ | Hook | Purpose | Related Issue |
6
+ |------|---------|---------------|
7
+ | **allowlist.sh** | Block everything not explicitly approved (inverse model) | [#37471](https://github.com/anthropics/claude-code/issues/37471) |
8
+ | **auto-approve-build.sh** | Auto-approve npm/yarn/cargo/go build, test, lint | |
9
+ | **auto-approve-docker.sh** | Auto-approve docker build, compose, ps, logs | |
10
+ | **auto-approve-git-read.sh** | Auto-approve `git status/log/diff` even with `-C` flags | [#36900](https://github.com/anthropics/claude-code/issues/36900) |
11
+ | **auto-approve-python.sh** | Auto-approve pytest, mypy, ruff, black, isort | |
12
+ | **auto-approve-ssh.sh** | Auto-approve safe SSH commands (uptime, whoami) | |
13
+ | **auto-snapshot.sh** | Save file snapshots before edits (rollback protection) | [#37386](https://github.com/anthropics/claude-code/issues/37386) |
14
+ | **block-database-wipe.sh** | Block destructive DB commands (Laravel, Django, Rails) | [#37405](https://github.com/anthropics/claude-code/issues/37405) |
15
+ | **edit-guard.sh** | Block Edit/Write to protected files | [#37210](https://github.com/anthropics/claude-code/issues/37210) |
16
+ | **enforce-tests.sh** | Warn when source changes without test changes | |
17
+ | **notify-waiting.sh** | Desktop notification when Claude waits for input | |
18
+ | **protect-dotfiles.sh** | Block modifications to ~/.bashrc, ~/.aws/, ~/.ssh/ | [#37478](https://github.com/anthropics/claude-code/issues/37478) |
19
+ | **scope-guard.sh** | Block file operations outside project directory | [#36233](https://github.com/anthropics/claude-code/issues/36233) |
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # 1. Copy example to hooks directory
25
+ cp examples/block-database-wipe.sh ~/.claude/hooks/
26
+
27
+ # 2. Make executable
28
+ chmod +x ~/.claude/hooks/block-database-wipe.sh
29
+
30
+ # 3. Add to settings.json
31
+ # See each file's header comment for the JSON configuration
32
+ ```
33
+
34
+ ## List from CLI
35
+
36
+ ```bash
37
+ npx cc-safe-setup --examples
38
+ ```
@@ -0,0 +1,64 @@
1
+ #!/bin/bash
2
+ # allowlist.sh — Only allow explicitly approved commands
3
+ #
4
+ # Inverts the default permission model: everything is blocked
5
+ # unless it matches an approved pattern. This is the opposite
6
+ # of cc-safe-setup's destructive-guard (which blocks specific
7
+ # dangerous commands).
8
+ #
9
+ # Use case: Highly sensitive environments where you want to
10
+ # enumerate exactly what Claude Code can do.
11
+ #
12
+ # Born from GitHub Issue #37471 (Immutable session manifest)
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/allowlist.sh" }]
21
+ # }]
22
+ # }
23
+ # }
24
+
25
+ INPUT=$(cat)
26
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
27
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
28
+
29
+ # Only gate Bash commands
30
+ [[ "$TOOL" != "Bash" ]] && exit 0
31
+ [[ -z "$COMMAND" ]] && exit 0
32
+
33
+ # ========================================
34
+ # ALLOWLIST — add your approved patterns
35
+ # ========================================
36
+ ALLOWED=(
37
+ # Git (read-only + commit, but not push/reset/clean)
38
+ "^\s*git (add|commit|diff|log|status|branch|show|stash|rev-parse|tag)"
39
+ # Package managers (install + read-only)
40
+ "^\s*npm (test|run|install|ci|ls|outdated)"
41
+ "^\s*pip (install|list|show|freeze)"
42
+ # Build/test/lint
43
+ "^\s*pytest"
44
+ "^\s*python3? -m (pytest|py_compile|unittest)"
45
+ "^\s*node --check"
46
+ "^\s*(ruff|black|isort|flake8|pylint|mypy|eslint|prettier)"
47
+ # Safe read-only commands
48
+ "^\s*(cat|head|tail|wc|sort|grep|find|ls|pwd|echo|date|which|whoami)"
49
+ "^\s*(curl -s|wget -q)"
50
+ # Directory navigation
51
+ "^\s*(cd|mkdir|touch)"
52
+ )
53
+
54
+ for pattern in "${ALLOWED[@]}"; do
55
+ if echo "$COMMAND" | grep -qE "$pattern"; then
56
+ exit 0 # Approved
57
+ fi
58
+ done
59
+
60
+ # Not in allowlist — block
61
+ echo "BLOCKED: Command not in allowlist" >&2
62
+ echo "Command: $COMMAND" >&2
63
+ echo "To approve, add a pattern to ~/.claude/hooks/allowlist.sh" >&2
64
+ exit 2
@@ -0,0 +1,81 @@
1
+ #!/bin/bash
2
+ # protect-dotfiles.sh — Block destructive operations on home directory config files
3
+ #
4
+ # Solves: Claude Code overwriting .bashrc, deleting .aws/, running
5
+ # chezmoi/stow without diffing first (#37478, #33391)
6
+ #
7
+ # Covers: Edit/Write to dotfiles, Bash commands that modify them
8
+ #
9
+ # Usage: Add to settings.json as a PreToolUse hook
10
+ #
11
+ # {
12
+ # "hooks": {
13
+ # "PreToolUse": [{
14
+ # "matcher": "",
15
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/protect-dotfiles.sh" }]
16
+ # }]
17
+ # }
18
+ # }
19
+
20
+ INPUT=$(cat)
21
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
22
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
23
+ CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
24
+
25
+ # === Check 1: Edit/Write to dotfiles ===
26
+ if [[ "$TOOL" == "Edit" || "$TOOL" == "Write" ]]; then
27
+ HOME_DIR=$(eval echo "~")
28
+ PROTECTED_DOTFILES=(
29
+ ".bashrc" ".bash_profile" ".bash_logout"
30
+ ".zshrc" ".zprofile" ".zshenv"
31
+ ".profile" ".login"
32
+ ".gitconfig" ".gitignore_global"
33
+ ".ssh/config" ".ssh/authorized_keys"
34
+ ".aws/config" ".aws/credentials"
35
+ ".npmrc" ".yarnrc"
36
+ ".env" ".env.local" ".env.production"
37
+ )
38
+ for dotfile in "${PROTECTED_DOTFILES[@]}"; do
39
+ if [[ "$FILE" == "${HOME_DIR}/${dotfile}" ]]; then
40
+ echo "BLOCKED: Cannot modify ~/${dotfile}" >&2
41
+ echo "This is a critical config file. Edit it manually." >&2
42
+ exit 2
43
+ fi
44
+ done
45
+ # Block writing to any ~/.ssh/ or ~/.aws/ files
46
+ if [[ "$FILE" == "${HOME_DIR}/.ssh/"* || "$FILE" == "${HOME_DIR}/.aws/"* ]]; then
47
+ echo "BLOCKED: Cannot modify files in ${FILE%/*}/" >&2
48
+ exit 2
49
+ fi
50
+ fi
51
+
52
+ # === Check 2: Bash commands that modify dotfiles ===
53
+ if [[ "$TOOL" == "Bash" && -n "$CMD" ]]; then
54
+ # Skip echo/printf (string output, not actual modification)
55
+ if echo "$CMD" | grep -qE '^\s*(echo|printf|cat\s*<<)'; then
56
+ exit 0
57
+ fi
58
+
59
+ # Block chezmoi/stow apply without --dry-run
60
+ if echo "$CMD" | grep -qE '(chezmoi\s+(init|apply|update)|stow\s)' && \
61
+ ! echo "$CMD" | grep -qE '(--dry-run|--diff|-n\b|diff)'; then
62
+ echo "BLOCKED: Run 'chezmoi diff' or '--dry-run' first" >&2
63
+ echo "Command: $CMD" >&2
64
+ exit 2
65
+ fi
66
+
67
+ # Block rm on dotfile directories
68
+ if echo "$CMD" | grep -qE 'rm\s.*\.(ssh|aws|gnupg|config|local)'; then
69
+ echo "BLOCKED: Cannot delete dotfile directory" >&2
70
+ exit 2
71
+ fi
72
+
73
+ # Block cp/mv overwriting dotfiles without backup
74
+ if echo "$CMD" | grep -qE '(cp|mv)\s.*\.(bashrc|zshrc|profile|gitconfig)' && \
75
+ ! echo "$CMD" | grep -qE '(--backup|-b)'; then
76
+ echo "BLOCKED: Use --backup flag when overwriting dotfiles" >&2
77
+ exit 2
78
+ fi
79
+ fi
80
+
81
+ exit 0
@@ -0,0 +1,56 @@
1
+ #!/bin/bash
2
+ # scope-guard.sh — Block file operations outside the project directory
3
+ #
4
+ # Solves: Claude Code deleting files on Desktop, in ~/Applications,
5
+ # or anywhere outside the working directory (#36233, #36339)
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/scope-guard.sh" }]
14
+ # }]
15
+ # }
16
+ # }
17
+
18
+ INPUT=$(cat)
19
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
20
+ CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
21
+
22
+ [[ "$TOOL" != "Bash" ]] && exit 0
23
+ [[ -z "$CMD" ]] && exit 0
24
+
25
+ # Skip string output commands
26
+ if echo "$CMD" | grep -qE '^\s*(echo|printf|cat\s*<<)'; then
27
+ exit 0
28
+ fi
29
+
30
+ # Check for destructive commands with paths outside project
31
+ if echo "$CMD" | grep -qE '\brm\b.*(-[a-zA-Z]*[rf]|--(recursive|force))'; then
32
+ # Block absolute paths
33
+ if echo "$CMD" | grep -qE '\brm\b[^|;]*\s+/[a-zA-Z]'; then
34
+ echo "BLOCKED: rm with absolute path" >&2
35
+ echo "Command: $CMD" >&2
36
+ exit 2
37
+ fi
38
+ # Block home directory paths
39
+ if echo "$CMD" | grep -qE '\brm\b[^|;]*\s+~/'; then
40
+ echo "BLOCKED: rm targeting home directory" >&2
41
+ exit 2
42
+ fi
43
+ # Block parent directory escapes
44
+ if echo "$CMD" | grep -qE '\brm\b[^|;]*\s+\.\./'; then
45
+ echo "BLOCKED: rm escaping project directory" >&2
46
+ exit 2
47
+ fi
48
+ fi
49
+
50
+ # Block targeting well-known user/system directories
51
+ if echo "$CMD" | grep -qiE '\b(rm|del|Remove-Item)\b.*(Desktop|Applications|Documents|Downloads|Library|Keychain|\.aws|\.ssh)'; then
52
+ echo "BLOCKED: targeting system/user directory" >&2
53
+ exit 2
54
+ fi
55
+
56
+ exit 0
package/index.mjs CHANGED
@@ -263,6 +263,9 @@ function examples() {
263
263
  'notify-waiting.sh': 'Desktop notification when Claude waits for input',
264
264
  'auto-approve-python.sh': 'Auto-approve pytest, mypy, ruff, black, isort commands',
265
265
  'auto-snapshot.sh': 'Auto-save file snapshots before edits (rollback protection)',
266
+ 'allowlist.sh': 'Block everything not in allowlist (inverse permission model)',
267
+ 'protect-dotfiles.sh': 'Block modifications to ~/.bashrc, ~/.aws/, ~/.ssh/',
268
+ 'scope-guard.sh': 'Block file operations outside project directory',
266
269
  };
267
270
 
268
271
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
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": {