cc-safe-setup 29.3.1 → 29.5.0

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
@@ -12,7 +12,7 @@ npx cc-safe-setup
12
12
 
13
13
  Installs 8 safety hooks in ~10 seconds. Blocks `rm -rf /`, prevents pushes to main, catches secret leaks, validates syntax after every edit. Zero dependencies.
14
14
 
15
- [**Getting Started**](https://yurukusa.github.io/cc-safe-setup/getting-started.html) · [**All Tools**](https://yurukusa.github.io/cc-safe-setup/hub.html) · [**Recipes**](https://yurukusa.github.io/cc-safe-setup/recipes.html) · [Validate your settings.json](https://yurukusa.github.io/cc-safe-setup/validator.html)
15
+ [**Getting Started**](https://yurukusa.github.io/cc-safe-setup/getting-started.html) · [**All Tools**](https://yurukusa.github.io/cc-safe-setup/hub.html) · [**Recipes**](https://yurukusa.github.io/cc-safe-setup/recipes.html) · [Validate your settings.json](https://yurukusa.github.io/cc-safe-setup/validator.html) · [**Check your score**](https://yurukusa.github.io/cc-health-check/) (`npx cc-health-check`)
16
16
 
17
17
  ```
18
18
  cc-safe-setup
@@ -49,6 +49,8 @@ A Claude Code user [lost their entire C:\Users directory](https://github.com/ant
49
49
 
50
50
  Claude Code ships with no safety hooks by default. This tool fixes that.
51
51
 
52
+ **Works with Auto Mode.** Claude Code's [Auto Mode sandboxing](https://www.anthropic.com/engineering/claude-code-sandboxing) provides container-level isolation. cc-safe-setup adds process-level hooks as defense-in-depth — catching destructive commands even outside sandboxed environments.
53
+
52
54
  ## What Gets Installed
53
55
 
54
56
  | Hook | Prevents | Related Issues |
@@ -87,7 +89,7 @@ Each hook exists because a real incident happened without it.
87
89
  | `--scan [--apply]` | Tech stack detection |
88
90
  | `--export / --import` | Team config sharing |
89
91
  | `--verify` | Test each hook |
90
- | `--install-example <name>` | Install from 338 examples |
92
+ | `--install-example <name>` | Install from 335 examples |
91
93
  | `--examples [filter]` | Browse examples by keyword |
92
94
  | `--full` | All-in-one setup |
93
95
  | `--status` | Check installed hooks |
@@ -211,11 +213,11 @@ npx cc-health-check
211
213
 
212
214
  ## Full Kit
213
215
 
214
- cc-safe-setup gives you 8 essential hooks. For the complete autonomous operation toolkit:
216
+ cc-safe-setup gives you 8 essential hooks. Want to know what else your setup needs?
215
217
 
216
- **[Claude Code Ops Kit](https://yurukusa.github.io/cc-ops-kit-landing/?utm_source=github&utm_medium=readme&utm_campaign=safe-setup)** — 16 hooks + 5 templates + 3 exclusive tools + install.sh. Production-ready in 15 minutes.
218
+ Run `npx cc-health-check` (free, 20 checks) to see your current score. If it's below 80, the **[Claude Code Ops Kit](https://yurukusa.github.io/cc-ops-kit-landing/?utm_source=github&utm_medium=readme&utm_campaign=safe-setup)** fills the gaps — 16 hooks + 6 templates + 3 exclusive tools + install.sh. Pay What You Want.
217
219
 
218
- Or start with the free hooks: [claude-code-hooks](https://github.com/yurukusa/claude-code-hooks)
220
+ Or browse the free hooks: [claude-code-hooks](https://github.com/yurukusa/claude-code-hooks)
219
221
 
220
222
  ## Examples
221
223
 
@@ -368,6 +370,9 @@ See [Issue #1](https://github.com/yurukusa/cc-safe-setup/issues/1) for details.
368
370
  - [Hooks Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/cheatsheet.html) — printable A4 quick reference
369
371
  - [Ecosystem Comparison](https://yurukusa.github.io/cc-safe-setup/ecosystem.html) — all Claude Code hook projects compared
370
372
  - [The incident that inspired this tool](https://github.com/anthropics/claude-code/issues/36339) — NTFS junction rm -rf
373
+ - [How to prevent rm -rf disasters](https://yurukusa.github.io/cc-safe-setup/prevent-rm-rf.html) — real incidents and the hook that stops them
374
+ - [How to prevent force-push to main](https://yurukusa.github.io/cc-safe-setup/prevent-force-push.html) — branch protection via hooks
375
+ - [How to prevent secret leaks](https://yurukusa.github.io/cc-safe-setup/prevent-secret-leaks.html) — stop git add . from committing .env
371
376
 
372
377
  ## FAQ
373
378
 
@@ -387,6 +392,8 @@ No. Each hook runs in ~10ms. They only fire on specific events (before tool use,
387
392
 
388
393
  Found a false positive? Open an [issue](https://github.com/yurukusa/cc-safe-setup/issues/new?template=false_positive.md). Want a new hook? Open a [feature request](https://github.com/yurukusa/cc-safe-setup/issues/new?template=bug_report.md).
389
394
 
395
+ 📘 **Want the full story?** [Production guide from 700+ hours of autonomous operation](https://zenn.dev/yurukusa/books/6076c23b1cb18b) — the incidents, fixes, and patterns behind every hook in this tool.
396
+
390
397
  If cc-safe-setup saved your project from a destructive command, consider giving it a star — it helps others find this tool.
391
398
 
392
399
  ## License
@@ -160,6 +160,19 @@ exit 0
160
160
 
161
161
  **Rule of thumb:** PreToolUse = block dangerous actions. PermissionRequest = allow trusted actions that trigger built-in prompts.
162
162
 
163
+ ## "PermissionRequest hooks don't fire in `-p` mode"
164
+
165
+ **Known limitation** ([#35646](https://github.com/anthropics/claude-code/issues/35646)): In headless/pipe mode (`claude -p`), the protected-directory check short-circuits *before* PermissionRequest hooks fire. This means:
166
+
167
+ | Mode | PermissionRequest fires? | Hook workaround works? |
168
+ |------|-------------------------|----------------------|
169
+ | Interactive (`claude`) | ✅ Yes | ✅ Yes |
170
+ | Interactive + bypassPermissions | ✅ Yes | ✅ Yes |
171
+ | Pipe mode (`claude -p`) | ❌ No | ❌ No |
172
+ | Pipe + `--dangerously-skip-permissions` | ❌ No | ❌ No |
173
+
174
+ **Workaround:** Currently none for `-p` mode. If your automation needs to write to `.claude/`, use interactive mode with hooks instead. This is a Claude Code core issue — the fix requires the harness to route protected-dir checks through PermissionRequest in all modes.
175
+
163
176
  ## "Permission prompts still appear for compound commands"
164
177
 
165
178
  This is a known Claude Code limitation, not a hook issue. `Bash(git:*)` doesn't match `cd /path && git log`.
@@ -0,0 +1,108 @@
1
+ #!/bin/bash
2
+ # auto-mode-safe-commands.sh — Fix Auto Mode false positives on safe commands
3
+ #
4
+ # Solves: Claude Code's safety classifier blocks legitimate commands in auto mode
5
+ # - $() command substitution flagged as dangerous (#38537, 49 reactions)
6
+ # - Pipe chains flagged unnecessarily (#30435, 29 reactions)
7
+ # - Read-only commands requiring manual approval
8
+ #
9
+ # How it works: Maintains a whitelist of known-safe command patterns.
10
+ # When the classifier wrongly blocks them, this hook approves.
11
+ # Only approves commands that are genuinely read-only or development-safe.
12
+ #
13
+ # Usage: Add to settings.json as a PreToolUse hook
14
+ #
15
+ # {
16
+ # "hooks": {
17
+ # "PreToolUse": [{
18
+ # "matcher": "Bash",
19
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/auto-mode-safe-commands.sh" }]
20
+ # }]
21
+ # }
22
+ # }
23
+
24
+ INPUT=$(cat)
25
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
26
+
27
+ [ -z "$COMMAND" ] && exit 0
28
+
29
+ # Strip the command to its base (first word after pipes, &&, etc.)
30
+ # We check each component of compound commands
31
+ APPROVE=false
32
+ REASON=""
33
+
34
+ # --- Read-only commands (never modify state) ---
35
+
36
+ # File inspection
37
+ if echo "$COMMAND" | grep -qE '^\s*(cat|head|tail|less|more|wc|file|stat|du|df|ls|tree|find|which|whereis|type|realpath|readlink)\s'; then
38
+ APPROVE=true
39
+ REASON="Read-only file inspection"
40
+ fi
41
+
42
+ # Text search
43
+ if echo "$COMMAND" | grep -qE '^\s*(grep|rg|ag|ack|sed\s+-n|awk)\s'; then
44
+ APPROVE=true
45
+ REASON="Text search/extraction"
46
+ fi
47
+
48
+ # Git read-only
49
+ if echo "$COMMAND" | grep -qE '^\s*git\s+(status|log|diff|show|branch|tag|remote|stash\s+list|ls-files|ls-tree|rev-parse|describe|shortlog|blame|config\s+--get)'; then
50
+ APPROVE=true
51
+ REASON="Git read-only operation"
52
+ fi
53
+
54
+ # Package info (read-only)
55
+ if echo "$COMMAND" | grep -qE '^\s*(npm\s+(ls|list|info|view|outdated|audit)|pip\s+(list|show|freeze)|yarn\s+(list|info|why)|pnpm\s+(ls|list))\s*'; then
56
+ APPROVE=true
57
+ REASON="Package manager read-only"
58
+ fi
59
+
60
+ # Development tools (safe)
61
+ if echo "$COMMAND" | grep -qE '^\s*(echo|printf|date|env|printenv|uname|hostname|whoami|id|pwd|tput)\s*'; then
62
+ APPROVE=true
63
+ REASON="Environment inspection"
64
+ fi
65
+
66
+ # --- Safe command substitution patterns ---
67
+ # $() is flagged by classifier but usually wraps read-only commands
68
+
69
+ # date/timestamp substitution
70
+ if echo "$COMMAND" | grep -qE '\$\(date\s'; then
71
+ # Only approve if the outer command is also safe
72
+ OUTER=$(echo "$COMMAND" | sed 's/\$([^)]*)/SUBST/g')
73
+ if echo "$OUTER" | grep -qE '^\s*(echo|printf|mkdir|touch|cp|mv)\s'; then
74
+ APPROVE=true
75
+ REASON="Safe command with date substitution"
76
+ fi
77
+ fi
78
+
79
+ # --- JSON/YAML processing ---
80
+ if echo "$COMMAND" | grep -qE '^\s*(jq|yq|python3?\s+-c\s|python3?\s+-m\s+json)\s'; then
81
+ APPROVE=true
82
+ REASON="JSON/YAML processing"
83
+ fi
84
+
85
+ # --- Curl (read-only GET requests) ---
86
+ if echo "$COMMAND" | grep -qE '^\s*curl\s+-s' && ! echo "$COMMAND" | grep -qE '\s-X\s+(POST|PUT|PATCH|DELETE)'; then
87
+ APPROVE=true
88
+ REASON="HTTP GET request"
89
+ fi
90
+
91
+ # --- Node.js/Python one-liners ---
92
+ if echo "$COMMAND" | grep -qE '^\s*(node|python3?)\s+-e\s'; then
93
+ # Only approve if no file system writes detected
94
+ if ! echo "$COMMAND" | grep -qE '(writeFile|fs\.write|open\(.*["\x27]w|unlink|rmdir)'; then
95
+ APPROVE=true
96
+ REASON="Script one-liner (no fs writes detected)"
97
+ fi
98
+ fi
99
+
100
+ # --- Output the decision ---
101
+ if [ "$APPROVE" = true ]; then
102
+ jq -n --arg reason "$REASON" \
103
+ '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":$reason}}'
104
+ exit 0
105
+ fi
106
+
107
+ # No opinion — let the default classifier handle it
108
+ exit 0
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ # checkpoint-tamper-guard.sh — Block manipulation of hook state/checkpoint files
3
+ # Trigger: PreToolUse (Bash, Edit, Write)
4
+ # Prevents the model from bypassing hooks by editing their state files
5
+ # See: https://github.com/anthropics/claude-code/issues/38841
6
+
7
+ INPUT=$(cat)
8
+ CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
9
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
10
+
11
+ # Directories/files containing hook state (customize as needed)
12
+ PROTECTED_PATTERNS=(
13
+ ".claude/checkpoints"
14
+ ".claude/hook-state"
15
+ ".claude/hooks-disabled"
16
+ "session-call-count"
17
+ "compact-prep-done"
18
+ "subagent-tracker"
19
+ )
20
+
21
+ check_path() {
22
+ local path="$1"
23
+ for pattern in "${PROTECTED_PATTERNS[@]}"; do
24
+ if [[ "$path" == *"$pattern"* ]]; then
25
+ echo "BLOCKED: Cannot manipulate hook state file: $path" >&2
26
+ echo "Hook state files are managed by hooks, not by the model." >&2
27
+ exit 2
28
+ fi
29
+ done
30
+ }
31
+
32
+ # Check Bash commands that write to protected paths
33
+ if [ -n "$CMD" ]; then
34
+ for pattern in "${PROTECTED_PATTERNS[@]}"; do
35
+ if echo "$CMD" | grep -qE "(echo|cat|tee|cp|mv|rm|chmod|chown|touch|truncate|>).*${pattern}"; then
36
+ echo "BLOCKED: Cannot manipulate hook state via command" >&2
37
+ exit 2
38
+ fi
39
+ done
40
+ fi
41
+
42
+ # Check Edit/Write file paths
43
+ if [ -n "$FILE" ]; then
44
+ check_path "$FILE"
45
+ fi
46
+
47
+ exit 0
@@ -0,0 +1,163 @@
1
+ #!/bin/bash
2
+ # compound-command-allow.sh — Auto-approve compound commands when all parts are safe
3
+ #
4
+ # Solves: Permission prompts fire for compound commands like:
5
+ # cd /path && git log (#16561, 115 reactions)
6
+ # echo foo | grep bar (#28240, 84 reactions)
7
+ # npm test && npm run build (#30519, 58 reactions)
8
+ #
9
+ # How it works: Splits compound commands on &&, ||, ;, and |
10
+ # Checks each component against a safe-command whitelist.
11
+ # If ALL components are safe, auto-approves the entire command.
12
+ # If ANY component is unsafe, passes through (no opinion).
13
+ #
14
+ # This extends cd-git-allow to handle arbitrary compound commands.
15
+ #
16
+ # Usage: Add to settings.json as a PreToolUse hook
17
+ #
18
+ # {
19
+ # "hooks": {
20
+ # "PreToolUse": [{
21
+ # "matcher": "Bash",
22
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/compound-command-allow.sh" }]
23
+ # }]
24
+ # }
25
+ # }
26
+
27
+ INPUT=$(cat)
28
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
29
+
30
+ [ -z "$COMMAND" ] && exit 0
31
+
32
+ # Strip comments (lines starting with #) to avoid false matches
33
+ CLEAN=$(echo "$COMMAND" | sed 's/#.*$//' | tr '\n' ' ')
34
+
35
+ # Split on &&, ||, ;, and | (but not || inside [[ ]])
36
+ # Simple approach: split on these operators and check each part
37
+ IFS_ORIG="$IFS"
38
+
39
+ # Replace compound operators with a delimiter
40
+ PARTS=$(echo "$CLEAN" | sed 's/\s*&&\s*/\n/g; s/\s*||\s*/\n/g; s/\s*;\s*/\n/g; s/\s*|\s*/\n/g')
41
+
42
+ ALL_SAFE=true
43
+
44
+ while IFS= read -r part; do
45
+ # Trim whitespace
46
+ part=$(echo "$part" | sed 's/^\s*//;s/\s*$//')
47
+ [ -z "$part" ] && continue
48
+
49
+ # Extract the base command (first word)
50
+ BASE=$(echo "$part" | awk '{print $1}')
51
+
52
+ # Check against safe command list
53
+ case "$BASE" in
54
+ # Navigation
55
+ cd|pushd|popd|pwd)
56
+ ;;
57
+ # File reading
58
+ cat|head|tail|less|more|wc|file|stat|du|df|ls|tree|find|which|whereis|type|realpath|readlink|basename|dirname)
59
+ ;;
60
+ # Text processing (read-only)
61
+ grep|rg|ag|ack|sed|awk|sort|uniq|cut|tr|tee|xargs|column|fmt|fold|rev|nl|paste|join|comm)
62
+ # sed with -i is NOT read-only
63
+ if echo "$part" | grep -qE 'sed\s+.*-i'; then
64
+ ALL_SAFE=false
65
+ break
66
+ fi
67
+ ;;
68
+ # Git (read-only operations)
69
+ git)
70
+ SUBCMD=$(echo "$part" | awk '{print $2}')
71
+ case "$SUBCMD" in
72
+ status|log|diff|show|branch|tag|remote|stash|ls-files|ls-tree|rev-parse|describe|shortlog|blame|config|worktree)
73
+ # git stash with push/pop/drop is not read-only
74
+ if echo "$part" | grep -qE 'git\s+stash\s+(push|pop|drop|apply|clear)'; then
75
+ ALL_SAFE=false
76
+ break
77
+ fi
78
+ ;;
79
+ *)
80
+ ALL_SAFE=false
81
+ break
82
+ ;;
83
+ esac
84
+ ;;
85
+ # Node.js/npm (read-only)
86
+ node|npm|npx|yarn|pnpm)
87
+ SUBCMD=$(echo "$part" | awk '{print $2}')
88
+ case "$BASE" in
89
+ npm)
90
+ case "$SUBCMD" in
91
+ ls|list|info|view|outdated|audit|explain|why|help|config|prefix|root)
92
+ ;;
93
+ test|run)
94
+ ;; # npm test/run are generally safe
95
+ *)
96
+ ALL_SAFE=false
97
+ break
98
+ ;;
99
+ esac
100
+ ;;
101
+ node)
102
+ if echo "$part" | grep -qE 'node\s+-e\s'; then
103
+ if echo "$part" | grep -qE '(writeFile|fs\.write|unlink|rmdir|mkdirSync)'; then
104
+ ALL_SAFE=false
105
+ break
106
+ fi
107
+ elif echo "$part" | grep -qE 'node\s+-p\s'; then
108
+ : # node -p is safe (eval + print)
109
+ fi
110
+ ;;
111
+ *)
112
+ ;; # npx, yarn, pnpm — allow for now
113
+ esac
114
+ ;;
115
+ # Python (read-only)
116
+ python|python3)
117
+ if echo "$part" | grep -qE 'python3?\s+(-c|-m\s+(json|py_compile|compileall|ast|tokenize|dis|inspect))'; then
118
+ : # Safe one-liners
119
+ elif echo "$part" | grep -qE 'python3?\s+-m\s+pytest'; then
120
+ : # pytest is safe
121
+ else
122
+ ALL_SAFE=false
123
+ break
124
+ fi
125
+ ;;
126
+ # Shell builtins (safe)
127
+ echo|printf|true|false|test|\[|export|set|env|printenv|date|sleep|read|source|\.)
128
+ ;;
129
+ # System info
130
+ uname|hostname|whoami|id|groups|uptime|free|top|ps|lsb_release|arch|nproc|getconf)
131
+ ;;
132
+ # JSON/YAML processing
133
+ jq|yq)
134
+ ;;
135
+ # curl (GET only)
136
+ curl)
137
+ if echo "$part" | grep -qE '\s-X\s+(POST|PUT|PATCH|DELETE)'; then
138
+ ALL_SAFE=false
139
+ break
140
+ fi
141
+ ;;
142
+ # mkdir is generally safe
143
+ mkdir)
144
+ ;;
145
+ # touch is generally safe
146
+ touch)
147
+ ;;
148
+ *)
149
+ ALL_SAFE=false
150
+ break
151
+ ;;
152
+ esac
153
+ done <<< "$PARTS"
154
+
155
+ IFS="$IFS_ORIG"
156
+
157
+ if [ "$ALL_SAFE" = true ]; then
158
+ jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"All components of compound command are safe"}}'
159
+ exit 0
160
+ fi
161
+
162
+ # Unsafe component found — no opinion, let default handler decide
163
+ exit 0
@@ -0,0 +1,125 @@
1
+ #!/bin/bash
2
+ # write-secret-guard.sh — Block secrets from being written to files
3
+ #
4
+ # Solves: Claude writes API keys, tokens, and passwords directly into source files
5
+ # instead of using environment variables (#29910, 14 reactions)
6
+ # Existing secret-guard only covers Bash (git add .env).
7
+ # This hook covers Write and Edit tools.
8
+ #
9
+ # Detects: AWS keys (AKIA...), GitHub tokens (ghp_/gho_/ghs_),
10
+ # OpenAI keys (sk-), Anthropic keys (sk-ant-),
11
+ # Slack tokens (xoxb-/xoxp-), Stripe keys (sk_live_/pk_live_),
12
+ # Generic Bearer tokens, private keys, high-entropy strings
13
+ #
14
+ # Usage: Add to settings.json as a PreToolUse hook for Write AND Edit
15
+ #
16
+ # {
17
+ # "hooks": {
18
+ # "PreToolUse": [{
19
+ # "matcher": "Write",
20
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/write-secret-guard.sh" }]
21
+ # }, {
22
+ # "matcher": "Edit",
23
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/write-secret-guard.sh" }]
24
+ # }]
25
+ # }
26
+ # }
27
+
28
+ INPUT=$(cat)
29
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
30
+
31
+ # Get the content being written
32
+ if [ "$TOOL" = "Write" ]; then
33
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // empty' 2>/dev/null)
34
+ FILEPATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
35
+ elif [ "$TOOL" = "Edit" ]; then
36
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // empty' 2>/dev/null)
37
+ FILEPATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
38
+ else
39
+ exit 0
40
+ fi
41
+
42
+ [ -z "$CONTENT" ] && exit 0
43
+
44
+ # --- Allow known safe patterns ---
45
+
46
+ # Allow .env.example / .env.template (these contain placeholders)
47
+ if echo "$FILEPATH" | grep -qE '\.(example|template|sample)$'; then
48
+ exit 0
49
+ fi
50
+
51
+ # Allow test files
52
+ if echo "$FILEPATH" | grep -qE '(test|spec|mock|fixture|__test__|\.test\.)'; then
53
+ exit 0
54
+ fi
55
+
56
+ # --- Detect secret patterns ---
57
+
58
+ BLOCKED=""
59
+
60
+ # AWS Access Key ID (AKIA followed by 16 uppercase alphanumeric)
61
+ if echo "$CONTENT" | grep -qE 'AKIA[0-9A-Z]{16}'; then
62
+ BLOCKED="AWS Access Key ID (AKIA...)"
63
+ fi
64
+
65
+ # AWS Secret Access Key (40 char base64-like after specific prefixes)
66
+ if echo "$CONTENT" | grep -qE '(aws_secret_access_key|AWS_SECRET)\s*[=:]\s*[A-Za-z0-9/+=]{40}'; then
67
+ BLOCKED="AWS Secret Access Key"
68
+ fi
69
+
70
+ # GitHub tokens
71
+ if echo "$CONTENT" | grep -qE '(ghp_|gho_|ghs_|ghr_|github_pat_)[A-Za-z0-9_]{20,}'; then
72
+ BLOCKED="GitHub token"
73
+ fi
74
+
75
+ # OpenAI API key (sk-... or sk-proj-...)
76
+ if echo "$CONTENT" | grep -qE 'sk-[A-Za-z0-9_-]{20,}' && ! echo "$CONTENT" | grep -qE 'sk-ant-'; then
77
+ # Exclude Anthropic keys (handled separately)
78
+ BLOCKED="OpenAI API key (sk-...)"
79
+ fi
80
+
81
+ # Anthropic API key
82
+ if echo "$CONTENT" | grep -qE 'sk-ant-[A-Za-z0-9-]{20,}'; then
83
+ BLOCKED="Anthropic API key (sk-ant-...)"
84
+ fi
85
+
86
+ # Slack tokens
87
+ if echo "$CONTENT" | grep -qE '(xoxb-|xoxp-|xoxs-|xoxa-)[0-9A-Za-z-]{20,}'; then
88
+ BLOCKED="Slack token"
89
+ fi
90
+
91
+ # Stripe keys
92
+ if echo "$CONTENT" | grep -qE '(sk_live_|pk_live_|rk_live_)[A-Za-z0-9]{20,}'; then
93
+ BLOCKED="Stripe API key"
94
+ fi
95
+
96
+ # Google API key
97
+ if echo "$CONTENT" | grep -qE 'AIza[0-9A-Za-z_-]{35}'; then
98
+ BLOCKED="Google API key"
99
+ fi
100
+
101
+ # Private keys (PEM format)
102
+ if echo "$CONTENT" | grep -qE -- '-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----'; then
103
+ BLOCKED="Private key (PEM format)"
104
+ fi
105
+
106
+ # Bearer token assignment (not in comments or docs)
107
+ if echo "$CONTENT" | grep -qE '(Authorization|Bearer|token)\s*[=:]\s*["\x27][A-Za-z0-9._-]{30,}["\x27]' \
108
+ && ! echo "$FILEPATH" | grep -qiE '\.(md|txt|rst|adoc)$'; then
109
+ BLOCKED="Hardcoded Bearer/auth token"
110
+ fi
111
+
112
+ # Generic database connection strings with credentials
113
+ if echo "$CONTENT" | grep -qE '(mysql|postgres|mongodb|redis)://[^:]+:[^@]+@'; then
114
+ BLOCKED="Database connection string with credentials"
115
+ fi
116
+
117
+ # --- Block if secret detected ---
118
+
119
+ if [ -n "$BLOCKED" ]; then
120
+ echo "BLOCKED: Secret detected in file write — $BLOCKED" >&2
121
+ echo "Use environment variables instead: process.env.KEY or os.environ['KEY']" >&2
122
+ exit 2
123
+ fi
124
+
125
+ exit 0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "29.3.1",
4
- "description": "One command to make Claude Code safe. 346 hooks (8 built-in + 338 examples). 49 CLI commands. 1030 tests. 5 languages.",
3
+ "version": "29.5.0",
4
+ "description": "One command to make Claude Code safe. 335 example hooks + 8 built-in. 52 CLI commands. 1,760 tests. Works with Auto Mode.",
5
5
  "main": "index.mjs",
6
6
  "bin": {
7
7
  "cc-safe-setup": "index.mjs"
@@ -24,7 +24,14 @@
24
24
  "syntax-check",
25
25
  "context-window",
26
26
  "wsl",
27
- "wsl2"
27
+ "wsl2",
28
+ "auto-mode",
29
+ "defense-in-depth",
30
+ "force-push",
31
+ "secret-leak",
32
+ "destructive-command",
33
+ "claude-code-hooks",
34
+ "claude-code-safety"
28
35
  ],
29
36
  "scripts": {
30
37
  "test": "bash test.sh"