cc-safe-setup 1.1.1 → 1.1.3
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 +22 -1
- package/package.json +15 -2
- package/.github/FUNDING.yml +0 -3
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -20
- package/.github/ISSUE_TEMPLATE/false_positive.md +0 -12
- package/.github/workflows/test.yml +0 -14
- package/test.sh +0 -135
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
**One command to make Claude Code safe for autonomous operation.**
|
|
8
8
|
|
|
9
|
+
Not just a destructive command blocker — 7 hooks covering safety, quality, monitoring, and developer experience.
|
|
10
|
+
|
|
9
11
|
```bash
|
|
10
12
|
npx cc-safe-setup
|
|
11
13
|
```
|
|
@@ -73,6 +75,19 @@ Safe to run multiple times. Existing settings are preserved. A backup is created
|
|
|
73
75
|
|
|
74
76
|
**Requires:** [jq](https://jqlang.github.io/jq/) for JSON parsing (`brew install jq` / `apt install jq`).
|
|
75
77
|
|
|
78
|
+
## Before / After
|
|
79
|
+
|
|
80
|
+
Run `npx cc-health-check` to see the difference:
|
|
81
|
+
|
|
82
|
+
| | Before | After |
|
|
83
|
+
|---|--------|-------|
|
|
84
|
+
| Safety Guards | 25% | **75%** |
|
|
85
|
+
| Overall Score | 50/100 | **95/100** |
|
|
86
|
+
| Destructive commands | Unprotected | Blocked |
|
|
87
|
+
| Force push | Allowed | Blocked |
|
|
88
|
+
| `.env` in git | Possible | Blocked |
|
|
89
|
+
| Context warnings | None | 4-stage alerts |
|
|
90
|
+
|
|
76
91
|
## Configuration
|
|
77
92
|
|
|
78
93
|
| Variable | Hook | Default |
|
|
@@ -96,7 +111,7 @@ npx cc-health-check
|
|
|
96
111
|
|
|
97
112
|
cc-safe-setup gives you 7 essential hooks. For the complete autonomous operation toolkit:
|
|
98
113
|
|
|
99
|
-
**[Claude Code Ops Kit](https://yurukusa.github.io/cc-ops-kit-landing/?utm_source=github&utm_medium=readme&utm_campaign=safe-setup)** —
|
|
114
|
+
**[Claude Code Ops Kit](https://yurukusa.github.io/cc-ops-kit-landing/?utm_source=github&utm_medium=readme&utm_campaign=safe-setup)** — 16 hooks + 6 templates + 3 exclusive tools + install.sh. Production-ready in 15 minutes.
|
|
100
115
|
|
|
101
116
|
Or start with the free hooks: [claude-code-hooks](https://github.com/yurukusa/claude-code-hooks)
|
|
102
117
|
|
|
@@ -105,6 +120,12 @@ Or start with the free hooks: [claude-code-hooks](https://github.com/yurukusa/cl
|
|
|
105
120
|
- [Japanese guide (Qiita)](https://qiita.com/yurukusa/items/a9714b33f5d974e8f1e8) — この記事の日本語解説
|
|
106
121
|
- [The incident that inspired this tool](https://github.com/anthropics/claude-code/issues/36339) — NTFS junction rm -rf
|
|
107
122
|
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
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).
|
|
126
|
+
|
|
127
|
+
If cc-safe-setup saved your project from a destructive command, consider giving it a star — it helps others find this tool.
|
|
128
|
+
|
|
108
129
|
## License
|
|
109
130
|
|
|
110
131
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "One command to make Claude Code safe for autonomous operation. 7 hooks: destructive blocker, branch guard, force-push protection, secret leak prevention, syntax checks, and more.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"bin": {
|
|
@@ -8,12 +8,25 @@
|
|
|
8
8
|
},
|
|
9
9
|
"keywords": [
|
|
10
10
|
"claude-code",
|
|
11
|
+
"claude",
|
|
12
|
+
"anthropic",
|
|
11
13
|
"ai",
|
|
12
14
|
"safety",
|
|
13
15
|
"hooks",
|
|
14
16
|
"autonomous",
|
|
15
|
-
"cli"
|
|
17
|
+
"cli",
|
|
18
|
+
"pretooluse",
|
|
19
|
+
"guard",
|
|
20
|
+
"rm-rf",
|
|
21
|
+
"git-push",
|
|
22
|
+
"env",
|
|
23
|
+
"secrets",
|
|
24
|
+
"syntax-check",
|
|
25
|
+
"context-window"
|
|
16
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "bash test.sh"
|
|
29
|
+
},
|
|
17
30
|
"author": "yurukusa",
|
|
18
31
|
"license": "MIT",
|
|
19
32
|
"repository": {
|
package/.github/FUNDING.yml
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Bug Report
|
|
3
|
-
about: Something isn't working as expected
|
|
4
|
-
title: "[BUG] "
|
|
5
|
-
labels: bug
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
**What happened?**
|
|
9
|
-
|
|
10
|
-
**What did you expect?**
|
|
11
|
-
|
|
12
|
-
**Environment:**
|
|
13
|
-
- OS:
|
|
14
|
-
- Node version:
|
|
15
|
-
- Claude Code version:
|
|
16
|
-
- jq installed: yes/no
|
|
17
|
-
|
|
18
|
-
**Steps to reproduce:**
|
|
19
|
-
1. `npx cc-safe-setup`
|
|
20
|
-
2. ...
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
name: Tests
|
|
2
|
-
on: [push, pull_request]
|
|
3
|
-
jobs:
|
|
4
|
-
test:
|
|
5
|
-
runs-on: ubuntu-latest
|
|
6
|
-
steps:
|
|
7
|
-
- uses: actions/checkout@v4
|
|
8
|
-
- uses: actions/setup-node@v4
|
|
9
|
-
with:
|
|
10
|
-
node-version: '20'
|
|
11
|
-
- run: sudo apt-get install -y jq
|
|
12
|
-
- run: node index.mjs --help
|
|
13
|
-
- run: node index.mjs --dry-run
|
|
14
|
-
- run: bash test.sh
|
package/test.sh
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# cc-safe-setup hook tests
|
|
3
|
-
# Run: bash test.sh
|
|
4
|
-
# All hooks are tested by piping JSON input and checking exit codes
|
|
5
|
-
|
|
6
|
-
set -euo pipefail
|
|
7
|
-
|
|
8
|
-
PASS=0
|
|
9
|
-
FAIL=0
|
|
10
|
-
SCRIPTS_JSON="$(dirname "$0")/scripts.json"
|
|
11
|
-
|
|
12
|
-
# Extract hook scripts from scripts.json
|
|
13
|
-
extract_hook() {
|
|
14
|
-
python3 -c "import json; print(json.load(open('$SCRIPTS_JSON'))['$1'])" > "/tmp/test-$1.sh"
|
|
15
|
-
chmod +x "/tmp/test-$1.sh"
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
test_hook() {
|
|
19
|
-
local name="$1" input="$2" expected_exit="$3" desc="$4"
|
|
20
|
-
local actual_exit=0
|
|
21
|
-
echo "$input" | bash "/tmp/test-$name.sh" > /dev/null 2>/dev/null || actual_exit=$?
|
|
22
|
-
if [ "$actual_exit" -eq "$expected_exit" ]; then
|
|
23
|
-
echo " PASS: $desc"
|
|
24
|
-
PASS=$((PASS + 1))
|
|
25
|
-
else
|
|
26
|
-
echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
|
|
27
|
-
FAIL=$((FAIL + 1))
|
|
28
|
-
fi
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
echo "cc-safe-setup hook tests"
|
|
32
|
-
echo "========================"
|
|
33
|
-
echo ""
|
|
34
|
-
|
|
35
|
-
# --- destructive-guard ---
|
|
36
|
-
echo "destructive-guard:"
|
|
37
|
-
extract_hook "destructive-guard"
|
|
38
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"ls -la"}}' 0 "safe command passes"
|
|
39
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"rm -rf /"}}' 2 "rm -rf / blocked"
|
|
40
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"rm -rf ~/"}}' 2 "rm -rf ~/ blocked"
|
|
41
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"rm -rf ../"}}' 2 "rm -rf ../ blocked"
|
|
42
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"rm -rf node_modules"}}' 0 "rm -rf node_modules allowed"
|
|
43
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"git reset --hard"}}' 2 "git reset --hard blocked"
|
|
44
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"git reset --soft HEAD~1"}}' 0 "git reset --soft allowed"
|
|
45
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"git clean -fd"}}' 2 "git clean -fd blocked"
|
|
46
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"chmod -R 777 /"}}' 2 "chmod 777 / blocked"
|
|
47
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"find / -delete"}}' 2 "find / -delete blocked"
|
|
48
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"echo git reset --hard"}}' 0 "git reset in echo not blocked"
|
|
49
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"sudo rm -rf /home"}}' 2 "sudo rm -rf blocked"
|
|
50
|
-
test_hook "destructive-guard" '{"tool_input":{"command":"sudo apt install jq"}}' 0 "safe sudo command allowed"
|
|
51
|
-
echo ""
|
|
52
|
-
|
|
53
|
-
# --- branch-guard ---
|
|
54
|
-
echo "branch-guard:"
|
|
55
|
-
extract_hook "branch-guard"
|
|
56
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git push origin feature-branch"}}' 0 "push to feature allowed"
|
|
57
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git push -u origin my-branch"}}' 0 "push -u to branch allowed"
|
|
58
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git push origin main"}}' 2 "push to main blocked"
|
|
59
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git push origin master"}}' 2 "push to master blocked"
|
|
60
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git push --force origin feature"}}' 2 "force push blocked"
|
|
61
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git push -f origin feature"}}' 2 "force push -f blocked"
|
|
62
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git push --force-with-lease origin feature"}}' 2 "force-with-lease blocked"
|
|
63
|
-
test_hook "branch-guard" '{"tool_input":{"command":"git status"}}' 0 "non-push git command passes"
|
|
64
|
-
test_hook "branch-guard" '{"tool_input":{"command":"npm install"}}' 0 "non-git command passes"
|
|
65
|
-
echo ""
|
|
66
|
-
|
|
67
|
-
# --- secret-guard ---
|
|
68
|
-
echo "secret-guard:"
|
|
69
|
-
extract_hook "secret-guard"
|
|
70
|
-
test_hook "secret-guard" '{"tool_input":{"command":"git add src/index.js"}}' 0 "git add normal file allowed"
|
|
71
|
-
test_hook "secret-guard" '{"tool_input":{"command":"git add .env"}}' 2 "git add .env blocked"
|
|
72
|
-
test_hook "secret-guard" '{"tool_input":{"command":"git add .env.local"}}' 2 "git add .env.local blocked"
|
|
73
|
-
test_hook "secret-guard" '{"tool_input":{"command":"git add credentials.json"}}' 2 "git add credentials.json blocked"
|
|
74
|
-
test_hook "secret-guard" '{"tool_input":{"command":"git add id_rsa"}}' 2 "git add id_rsa blocked"
|
|
75
|
-
test_hook "secret-guard" '{"tool_input":{"command":"git add server.key"}}' 2 "git add .key file blocked"
|
|
76
|
-
test_hook "secret-guard" '{"tool_input":{"command":"npm install"}}' 0 "non-git command passes"
|
|
77
|
-
test_hook "secret-guard" '{"tool_input":{"command":"git commit -m test"}}' 0 "git commit passes"
|
|
78
|
-
echo ""
|
|
79
|
-
|
|
80
|
-
# --- comment-strip ---
|
|
81
|
-
echo "comment-strip:"
|
|
82
|
-
extract_hook "comment-strip"
|
|
83
|
-
# comment-strip outputs JSON on stdout when it modifies, exit 0 always
|
|
84
|
-
local_exit=0
|
|
85
|
-
result=$(echo '{"tool_input":{"command":"# check status\ngit status"}}' | bash /tmp/test-comment-strip.sh 2>/dev/null) || local_exit=$?
|
|
86
|
-
if [ "$local_exit" -eq 0 ] && echo "$result" | python3 -c "import json,sys; d=json.load(sys.stdin); assert 'git status' in d['hookSpecificOutput']['updatedInput']['command']" 2>/dev/null; then
|
|
87
|
-
echo " PASS: strips comment, returns clean command"
|
|
88
|
-
PASS=$((PASS + 1))
|
|
89
|
-
else
|
|
90
|
-
echo " FAIL: comment stripping"
|
|
91
|
-
FAIL=$((FAIL + 1))
|
|
92
|
-
fi
|
|
93
|
-
result2=$(echo '{"tool_input":{"command":"git status"}}' | bash /tmp/test-comment-strip.sh 2>/dev/null) || true
|
|
94
|
-
if [ -z "$result2" ]; then
|
|
95
|
-
echo " PASS: no-comment command passes through unchanged"
|
|
96
|
-
PASS=$((PASS + 1))
|
|
97
|
-
else
|
|
98
|
-
echo " FAIL: should pass through without modification"
|
|
99
|
-
FAIL=$((FAIL + 1))
|
|
100
|
-
fi
|
|
101
|
-
echo ""
|
|
102
|
-
|
|
103
|
-
# --- cd-git-allow ---
|
|
104
|
-
echo "cd-git-allow:"
|
|
105
|
-
extract_hook "cd-git-allow"
|
|
106
|
-
local_exit=0
|
|
107
|
-
result=$(echo '{"tool_input":{"command":"cd /tmp && git log"}}' | bash /tmp/test-cd-git-allow.sh 2>/dev/null) || local_exit=$?
|
|
108
|
-
if [ "$local_exit" -eq 0 ] && echo "$result" | grep -q "permissionDecision"; then
|
|
109
|
-
echo " PASS: cd+git log auto-approved"
|
|
110
|
-
PASS=$((PASS + 1))
|
|
111
|
-
else
|
|
112
|
-
echo " FAIL: cd+git log should be auto-approved"
|
|
113
|
-
FAIL=$((FAIL + 1))
|
|
114
|
-
fi
|
|
115
|
-
result2=$(echo '{"tool_input":{"command":"cd /tmp && git push origin main"}}' | bash /tmp/test-cd-git-allow.sh 2>/dev/null) || true
|
|
116
|
-
if ! echo "$result2" | grep -q "permissionDecision" 2>/dev/null; then
|
|
117
|
-
echo " PASS: cd+git push NOT auto-approved"
|
|
118
|
-
PASS=$((PASS + 1))
|
|
119
|
-
else
|
|
120
|
-
echo " FAIL: cd+git push should not be auto-approved"
|
|
121
|
-
FAIL=$((FAIL + 1))
|
|
122
|
-
fi
|
|
123
|
-
test_hook "cd-git-allow" '{"tool_input":{"command":"npm install"}}' 0 "non-cd command passes"
|
|
124
|
-
echo ""
|
|
125
|
-
|
|
126
|
-
# --- Summary ---
|
|
127
|
-
echo "========================"
|
|
128
|
-
TOTAL=$((PASS + FAIL))
|
|
129
|
-
echo "Results: $PASS/$TOTAL passed"
|
|
130
|
-
if [ "$FAIL" -gt 0 ]; then
|
|
131
|
-
echo "FAILURES: $FAIL"
|
|
132
|
-
exit 1
|
|
133
|
-
else
|
|
134
|
-
echo "All tests passed!"
|
|
135
|
-
fi
|