cc-safety-net 0.9.0 → 1.0.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 +352 -120
- package/dist/bin/cc-safety-net.js +7974 -4618
- package/dist/bin/commands/doctor.d.ts +11 -2
- package/dist/bin/commands/explain.d.ts +16 -2
- package/dist/bin/commands/hook.d.ts +14 -0
- package/dist/bin/commands/index.d.ts +67 -3
- package/dist/bin/commands/rule.d.ts +14 -0
- package/dist/bin/commands/statusline.d.ts +10 -2
- package/dist/bin/commands/types.d.ts +11 -0
- package/dist/bin/doctor/config.d.ts +1 -1
- package/dist/bin/doctor/hooks.d.ts +2 -1
- package/dist/bin/doctor/system-info.d.ts +6 -2
- package/dist/bin/doctor/types.d.ts +26 -1
- package/dist/bin/explain/config.d.ts +3 -1
- package/dist/bin/hook/common.d.ts +18 -0
- package/dist/bin/hook/config-edit.d.ts +11 -0
- package/dist/bin/hook/constants.d.ts +6 -0
- package/dist/bin/hook/install/kimi-cli.d.ts +3 -0
- package/dist/bin/hook/install/types.d.ts +4 -0
- package/dist/bin/hook/install.d.ts +3 -0
- package/dist/bin/hook/integrations.d.ts +12 -0
- package/dist/bin/hook/kimi-cli.d.ts +1 -0
- package/dist/bin/integration-metadata.d.ts +68 -0
- package/dist/bin/rule/doc.d.ts +1 -0
- package/dist/bin/rule/format.d.ts +14 -0
- package/dist/bin/rule/index.d.ts +1 -0
- package/dist/bin/rule/migrate.d.ts +6 -0
- package/dist/bin/rule/verify.d.ts +9 -0
- package/dist/builtin-commands/templates/cc-safety-net.d.ts +1 -0
- package/dist/core/analyze/analyze-command.d.ts +0 -10
- package/dist/core/analyze/awk.d.ts +3 -0
- package/dist/core/analyze/child-analyzer.d.ts +16 -0
- package/dist/core/analyze/child-command.d.ts +15 -0
- package/dist/core/analyze/find.d.ts +8 -6
- package/dist/core/analyze/index.d.ts +4 -0
- package/dist/core/analyze/parallel.d.ts +1 -0
- package/dist/core/{rules-rm.d.ts → analyze/rm.d.ts} +0 -1
- package/dist/core/analyze/shell-git-env.d.ts +10 -0
- package/dist/core/analyze/xargs.d.ts +1 -1
- package/dist/core/audit.d.ts +3 -0
- package/dist/core/config.d.ts +5 -3
- package/dist/core/env.d.ts +39 -1
- package/dist/core/format.d.ts +1 -0
- package/dist/core/git/config.d.ts +1 -0
- package/dist/core/git/env.d.ts +15 -0
- package/dist/core/git/index.d.ts +3 -0
- package/dist/core/git/parse.d.ts +9 -0
- package/dist/core/git/rules.d.ts +8 -0
- package/dist/core/git/worktree-relaxation.d.ts +11 -0
- package/dist/core/{worktree.d.ts → git/worktree.d.ts} +1 -5
- package/dist/core/rules/custom-rule-validation.d.ts +5 -0
- package/dist/core/rules/policy/config-file.d.ts +19 -0
- package/dist/core/rules/policy/index.d.ts +5 -0
- package/dist/core/rules/policy/lockfile.d.ts +5 -0
- package/dist/core/rules/policy/paths.d.ts +32 -0
- package/dist/core/rules/policy/resolver.d.ts +16 -0
- package/dist/core/rules/policy/scope-policy.d.ts +19 -0
- package/dist/core/rules/policy/sources.d.ts +28 -0
- package/dist/core/rules/policy/sync.d.ts +10 -0
- package/dist/core/rules/policy/types.d.ts +76 -0
- package/dist/core/rules/rulebook.d.ts +30 -0
- package/dist/core/shell/command.d.ts +2 -0
- package/dist/core/shell/index.d.ts +4 -0
- package/dist/core/shell/options.d.ts +3 -0
- package/dist/core/shell/segments.d.ts +6 -0
- package/dist/core/shell/shared.d.ts +4 -0
- package/dist/core/{shell.d.ts → shell/wrappers.d.ts} +0 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4205 -2231
- package/dist/{features → opencode}/builtin-commands/commands.d.ts +1 -1
- package/dist/opencode/builtin-commands/index.d.ts +2 -0
- package/dist/{features → opencode}/builtin-commands/types.d.ts +1 -1
- package/dist/pi/builtin-commands/commands.d.ts +16 -0
- package/dist/pi/builtin-commands/index.d.ts +1 -0
- package/dist/pi/index.d.ts +5 -0
- package/dist/pi/index.js +6432 -0
- package/dist/pi/tool-call.d.ts +21 -0
- package/dist/types.d.ts +32 -2
- package/package.json +16 -8
- package/dist/bin/commands/claude-code.d.ts +0 -2
- package/dist/bin/commands/copilot-cli.d.ts +0 -2
- package/dist/bin/commands/custom-rules-doc.d.ts +0 -2
- package/dist/bin/commands/gemini-cli.d.ts +0 -2
- package/dist/bin/commands/verify-config.d.ts +0 -2
- package/dist/bin/custom-rules-doc.d.ts +0 -1
- package/dist/bin/verify-config.d.ts +0 -12
- package/dist/core/analyze.d.ts +0 -21
- package/dist/core/rules-git.d.ts +0 -20
- package/dist/features/builtin-commands/index.d.ts +0 -2
- package/dist/features/builtin-commands/templates/set-custom-rules.d.ts +0 -1
- package/dist/features/builtin-commands/templates/verify-custom-rules.d.ts +0 -1
- /package/dist/bin/{hooks → hook}/claude-code.d.ts +0 -0
- /package/dist/bin/{hooks → hook}/copilot-cli.d.ts +0 -0
- /package/dist/bin/{hooks → hook}/gemini-cli.d.ts +0 -0
- /package/dist/core/{rules-custom.d.ts → rules/custom.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CC Safety Net
|
|
2
2
|
|
|
3
|
-
[](https://github.com/kenryu42/cc-safety-net/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/github/kenryu42/cc-safety-net)
|
|
5
|
+
[](https://github.com/kenryu42/cc-safety-net)
|
|
6
|
+
[](#codex-installation)
|
|
6
7
|
[](#claude-code-installation)
|
|
7
|
-
[](#opencode-installation)
|
|
8
|
-
[](#gemini-cli-installation)
|
|
9
8
|
[](#github-copilot-cli-installation)
|
|
10
|
-
[](#gemini-cli-installation)
|
|
10
|
+
[](#kimi-cli-installation)
|
|
11
|
+
[](#opencode-installation)
|
|
12
|
+
[](#pi-installation)
|
|
11
13
|
[](https://opensource.org/licenses/MIT)
|
|
12
14
|
|
|
13
15
|
<div align="center">
|
|
@@ -16,7 +18,7 @@
|
|
|
16
18
|
|
|
17
19
|
</div>
|
|
18
20
|
|
|
19
|
-
A
|
|
21
|
+
A Coding Agent CLI plugin that acts as a safety net, catching destructive git and filesystem commands before they execute.
|
|
20
22
|
|
|
21
23
|
## Contents
|
|
22
24
|
|
|
@@ -25,14 +27,14 @@ A Claude Code plugin that acts as a safety net, catching destructive git and fil
|
|
|
25
27
|
- [What About Sandboxing?](#what-about-sandboxing)
|
|
26
28
|
- [Prerequisites](#prerequisites)
|
|
27
29
|
- [Quick Start](#quick-start)
|
|
30
|
+
- [Codex Installation](#codex-installation)
|
|
28
31
|
- [Claude Code Installation](#claude-code-installation)
|
|
29
|
-
- [OpenCode Installation](#opencode-installation)
|
|
30
32
|
- [Gemini CLI Installation](#gemini-cli-installation)
|
|
31
33
|
- [GitHub Copilot CLI Installation](#github-copilot-cli-installation)
|
|
32
|
-
- [
|
|
34
|
+
- [Kimi CLI Installation](#kimi-cli-installation)
|
|
35
|
+
- [OpenCode Installation](#opencode-installation)
|
|
36
|
+
- [Pi Installation](#pi-installation)
|
|
33
37
|
- [Status Line Integration](#status-line-integration)
|
|
34
|
-
- [Setup via Slash Command](#setup-via-slash-command)
|
|
35
|
-
- [Manual Setup](#manual-setup)
|
|
36
38
|
- [Emoji Mode Indicators](#emoji-mode-indicators)
|
|
37
39
|
- [Diagnostics](#diagnostics)
|
|
38
40
|
- [Explain (Debug Analysis)](#explain-debug-analysis)
|
|
@@ -40,12 +42,12 @@ A Claude Code plugin that acts as a safety net, catching destructive git and fil
|
|
|
40
42
|
- [Commands Allowed](#commands-allowed)
|
|
41
43
|
- [What Happens When Blocked](#what-happens-when-blocked)
|
|
42
44
|
- [Testing the Hook](#testing-the-hook)
|
|
43
|
-
- [
|
|
44
|
-
- [Custom Rules
|
|
45
|
+
- [Breaking Change: Custom Rules Migration](#breaking-change-custom-rules-migration)
|
|
46
|
+
- [Custom Rules](#custom-rules)
|
|
45
47
|
- [Config File Location](#config-file-location)
|
|
46
48
|
- [Rule Schema](#rule-schema)
|
|
47
49
|
- [Matching Behavior](#matching-behavior)
|
|
48
|
-
- [Examples](#examples)
|
|
50
|
+
- [Rule Examples](#rule-examples)
|
|
49
51
|
- [Error Handling](#error-handling)
|
|
50
52
|
- [Advanced Features](#advanced-features)
|
|
51
53
|
- [Strict Mode](#strict-mode)
|
|
@@ -55,6 +57,7 @@ A Claude Code plugin that acts as a safety net, catching destructive git and fil
|
|
|
55
57
|
- [Interpreter One-Liner Detection](#interpreter-one-liner-detection)
|
|
56
58
|
- [Secret Redaction](#secret-redaction)
|
|
57
59
|
- [Audit Logging](#audit-logging)
|
|
60
|
+
- [Development](#development)
|
|
58
61
|
- [License](#license)
|
|
59
62
|
|
|
60
63
|
## Why This Exists
|
|
@@ -69,12 +72,12 @@ Claude Code's `.claude/settings.json` supports [deny rules](https://code.claude.
|
|
|
69
72
|
|
|
70
73
|
### At a Glance
|
|
71
74
|
|
|
72
|
-
| | Permission Deny Rules | Safety Net |
|
|
75
|
+
| | Permission Deny Rules | CC Safety Net |
|
|
73
76
|
|---|---|---|
|
|
74
77
|
| **Setup** | Manual configuration required | Works out of the box |
|
|
75
78
|
| **Parsing** | Wildcard pattern matching | Semantic command analysis |
|
|
76
79
|
| **Execution order** | Runs second | Runs first (PreToolUse hook) |
|
|
77
|
-
| **Shell wrappers** | Not handled automatically (must match wrapper forms) | Recursively analyzed (
|
|
80
|
+
| **Shell wrappers** | Not handled automatically (must match wrapper forms) | Recursively analyzed (up to 10 levels) |
|
|
78
81
|
| **Interpreter one-liners** | Not handled automatically (must match interpreter forms) | Detected and blocked |
|
|
79
82
|
|
|
80
83
|
### Permission Rules Have Known Bypass Vectors
|
|
@@ -89,9 +92,9 @@ Even with wildcard matching, Bash permission patterns are intentionally limited
|
|
|
89
92
|
| Extra whitespace | `rm -rf /` (double space) bypasses pattern |
|
|
90
93
|
| Shell wrappers | `sh -c "rm -rf /"` bypasses `Bash(rm:*)` entirely |
|
|
91
94
|
|
|
92
|
-
### Safety Net Handles What Patterns Can't
|
|
95
|
+
### CC Safety Net Handles What Patterns Can't
|
|
93
96
|
|
|
94
|
-
| Scenario | Permission Rules | Safety Net |
|
|
97
|
+
| Scenario | Permission Rules | CC Safety Net |
|
|
95
98
|
|----------|------------------|------------|
|
|
96
99
|
| `git checkout -b feature` (safe) | Blocked by `Bash(git checkout:*)` | Allowed |
|
|
97
100
|
| `git checkout -- file` (dangerous) | Blocked by `Bash(git checkout:*)` | Blocked |
|
|
@@ -102,17 +105,17 @@ Even with wildcard matching, Bash permission patterns are intentionally limited
|
|
|
102
105
|
|
|
103
106
|
### Defense in Depth
|
|
104
107
|
|
|
105
|
-
PreToolUse hooks run [**before**](https://code.claude.com/docs/en/iam#additional-permission-control-with-hooks) the permission system. This means Safety Net inspects every command first, regardless of your permission configuration. Even if you misconfigure deny rules, Safety Net provides a fallback layer of protection.
|
|
108
|
+
PreToolUse hooks run [**before**](https://code.claude.com/docs/en/iam#additional-permission-control-with-hooks) the permission system. This means CC Safety Net inspects every command first, regardless of your permission configuration. Even if you misconfigure deny rules, CC Safety Net provides a fallback layer of protection.
|
|
106
109
|
|
|
107
|
-
**Use both together**: Permission deny rules for quick, user-configurable blocks; Safety Net for robust, bypass-resistant protection that works out of the box.
|
|
110
|
+
**Use both together**: Permission deny rules for quick, user-configurable blocks; CC Safety Net for robust, bypass-resistant protection that works out of the box.
|
|
108
111
|
|
|
109
112
|
## What About Sandboxing?
|
|
110
113
|
|
|
111
|
-
Claude Code offers [native sandboxing](https://code.claude.com/docs/en/sandboxing) that provides OS-level filesystem and network isolation. Here's how it compares to Safety Net:
|
|
114
|
+
Claude Code offers [native sandboxing](https://code.claude.com/docs/en/sandboxing) that provides OS-level filesystem and network isolation. Here's how it compares to CC Safety Net:
|
|
112
115
|
|
|
113
116
|
### Different Layers of Protection
|
|
114
117
|
|
|
115
|
-
| | Sandboxing | Safety Net |
|
|
118
|
+
| | Sandboxing | CC Safety Net |
|
|
116
119
|
|---|---|---|
|
|
117
120
|
| **Enforcement** | OS-level (Seatbelt/bubblewrap) | Application-level (PreToolUse hook) |
|
|
118
121
|
| **Approach** | Containment — restricts filesystem + network access | Command analysis — blocks destructive operations |
|
|
@@ -128,7 +131,7 @@ Sandboxing restricts filesystem + network access, but it doesn't understand whet
|
|
|
128
131
|
> [!NOTE]
|
|
129
132
|
> Whether they're auto-run or require confirmation depends on your sandbox mode (auto-allow vs regular permissions), and network access still depends on your allowed-domain policy. Claude Code can also retry a command outside the sandbox via `dangerouslyDisableSandbox` (with user permission); this can be disabled with `allowUnsandboxedCommands: false`.
|
|
130
133
|
|
|
131
|
-
| Command | Sandboxing | Safety Net |
|
|
134
|
+
| Command | Sandboxing | CC Safety Net |
|
|
132
135
|
|---------|------------|------------|
|
|
133
136
|
| `git reset --hard` | Allowed (within cwd) | **Blocked** |
|
|
134
137
|
| `git checkout -- .` | Allowed (within cwd) | **Blocked** |
|
|
@@ -145,16 +148,16 @@ Sandboxing is the better choice when your primary concern is:
|
|
|
145
148
|
- **Prompt injection attacks** — Reduces exfiltration risk by restricting outbound domains (depends on your allowed-domain policy)
|
|
146
149
|
- **Malicious dependencies** — Limits filesystem writes and network access by default (subject to your sandbox configuration)
|
|
147
150
|
- **Untrusted code execution** — OS-level containment is stronger than pattern matching
|
|
148
|
-
- **Network control** — Safety Net has no network protection
|
|
151
|
+
- **Network control** — CC Safety Net has no network protection
|
|
149
152
|
|
|
150
153
|
### Recommended: Use Both
|
|
151
154
|
|
|
152
155
|
They protect against different threats:
|
|
153
156
|
|
|
154
157
|
- **Sandboxing** contains blast radius — even if something goes wrong, damage is limited to cwd and approved network domains
|
|
155
|
-
- **Safety Net** prevents footguns — catches git-specific mistakes that are technically "safe" from the sandbox's perspective
|
|
158
|
+
- **CC Safety Net** prevents footguns — catches git-specific mistakes that are technically "safe" from the sandbox's perspective
|
|
156
159
|
|
|
157
|
-
Running both together provides defense-in-depth. Sandboxing handles unknown threats; Safety Net handles known destructive patterns that sandboxing permits.
|
|
160
|
+
Running both together provides defense-in-depth. Sandboxing handles unknown threats; CC Safety Net handles known destructive patterns that sandboxing permits.
|
|
158
161
|
|
|
159
162
|
## Prerequisites
|
|
160
163
|
|
|
@@ -162,6 +165,29 @@ Running both together provides defense-in-depth. Sandboxing handles unknown thre
|
|
|
162
165
|
|
|
163
166
|
## Quick Start
|
|
164
167
|
|
|
168
|
+
### Codex Installation
|
|
169
|
+
|
|
170
|
+
1. Enable Codex plugin hooks in `~/.codex/config.toml`:
|
|
171
|
+
|
|
172
|
+
```toml
|
|
173
|
+
[features]
|
|
174
|
+
plugin_hooks = true
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
2. Add the marketplace:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
codex plugin marketplace add kenryu42/cc-marketplace
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
3. Start Codex.
|
|
184
|
+
4. In the TUI, run `/plugins`.
|
|
185
|
+
5. Use arrow keys to select `[cc-marketplace]`.
|
|
186
|
+
6. Press Enter to install the plugin.
|
|
187
|
+
7. run `/hooks` and select the safety-net PreToolUse hook and press `t` to trust it.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
165
191
|
### Claude Code Installation
|
|
166
192
|
|
|
167
193
|
```bash
|
|
@@ -176,72 +202,75 @@ Running both together provides defense-in-depth. Sandboxing handles unknown thre
|
|
|
176
202
|
|
|
177
203
|
---
|
|
178
204
|
|
|
179
|
-
###
|
|
180
|
-
|
|
181
|
-
**Option A: Let an LLM do it**
|
|
182
|
-
|
|
183
|
-
Paste this into any LLM agent (Claude Code, OpenCode, Cursor, etc.):
|
|
205
|
+
### Gemini CLI Installation
|
|
184
206
|
|
|
185
|
-
```
|
|
186
|
-
|
|
207
|
+
```bash
|
|
208
|
+
gemini extensions install https://github.com/kenryu42/gemini-safety-net
|
|
187
209
|
```
|
|
188
210
|
|
|
189
|
-
|
|
211
|
+
---
|
|
190
212
|
|
|
191
|
-
|
|
213
|
+
### GitHub Copilot CLI Installation
|
|
192
214
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
```
|
|
215
|
+
```bash
|
|
216
|
+
/plugin install kenryu42/copilot-safety-net
|
|
217
|
+
```
|
|
198
218
|
|
|
199
219
|
---
|
|
200
220
|
|
|
201
|
-
###
|
|
221
|
+
### Kimi CLI Installation
|
|
222
|
+
|
|
223
|
+
Install CC Safety Net into your Kimi CLI config:
|
|
202
224
|
|
|
203
225
|
```bash
|
|
204
|
-
|
|
226
|
+
npx -y cc-safety-net hook install --kimi-cli
|
|
205
227
|
```
|
|
206
228
|
|
|
207
229
|
---
|
|
208
230
|
|
|
209
|
-
|
|
231
|
+
|
|
232
|
+
### OpenCode Installation
|
|
233
|
+
|
|
234
|
+
Install CC Safety Net with OpenCode's native plugin command:
|
|
210
235
|
|
|
211
236
|
```bash
|
|
212
|
-
|
|
237
|
+
opencode plugin -g cc-safety-net
|
|
213
238
|
```
|
|
214
239
|
|
|
215
240
|
> [!NOTE]
|
|
216
|
-
>
|
|
241
|
+
> OpenCode can sometimes keep using a stale cached plugin version. See
|
|
242
|
+
> anomalyco/opencode#25293 for the current tracking issue.
|
|
243
|
+
>
|
|
244
|
+
> To force OpenCode to reinstall `cc-safety-net`, remove its cached package and
|
|
245
|
+
> install the version you want:
|
|
246
|
+
>
|
|
247
|
+
> ```sh
|
|
248
|
+
> rm -rf ~/.cache/opencode/packages/cc-safety-net@latest
|
|
249
|
+
> opencode plugin -g -f cc-safety-net@latest
|
|
250
|
+
>
|
|
251
|
+
> If you prefer pinning a specific version:
|
|
252
|
+
>
|
|
253
|
+
> rm -rf ~/.cache/opencode/packages/cc-safety-net@latest
|
|
254
|
+
> opencode plugin -g -f cc-safety-net@<version>
|
|
255
|
+
>
|
|
256
|
+
> Restart OpenCode after updating so the plugin is loaded from the refreshed
|
|
257
|
+
> cache.
|
|
217
258
|
|
|
218
259
|
---
|
|
219
260
|
|
|
220
|
-
###
|
|
221
|
-
|
|
222
|
-
1. Enable Codex plugin hooks in `~/.codex/config.toml`:
|
|
223
|
-
|
|
224
|
-
```toml
|
|
225
|
-
[features]
|
|
226
|
-
plugin_hooks = true
|
|
227
|
-
```
|
|
261
|
+
### Pi Installation
|
|
228
262
|
|
|
229
|
-
|
|
263
|
+
Install CC Safety Net with Pi's package installer:
|
|
230
264
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
3. Start Codex.
|
|
236
|
-
4. In the TUI, run `/plugins`.
|
|
237
|
-
5. Use arrow keys to select `[cc-marketplace]`.
|
|
238
|
-
6. Press Enter to install the plugin.
|
|
265
|
+
```bash
|
|
266
|
+
pi install npm:cc-safety-net
|
|
267
|
+
```
|
|
239
268
|
|
|
240
269
|
---
|
|
241
270
|
|
|
242
271
|
## Status Line Integration
|
|
243
272
|
|
|
244
|
-
Safety Net can display its status in Claude Code's status line, showing whether protection is active and which modes are enabled.
|
|
273
|
+
CC Safety Net can display its status in Claude Code's status line, showing whether protection is active and which modes are enabled.
|
|
245
274
|
|
|
246
275
|
Add the following to your `~/.claude/settings.json`:
|
|
247
276
|
|
|
@@ -251,7 +280,7 @@ Add the following to your `~/.claude/settings.json`:
|
|
|
251
280
|
{
|
|
252
281
|
"statusLine": {
|
|
253
282
|
"type": "command",
|
|
254
|
-
"command": "bunx cc-safety-net --
|
|
283
|
+
"command": "bunx cc-safety-net statusline --claude-code"
|
|
255
284
|
}
|
|
256
285
|
}
|
|
257
286
|
```
|
|
@@ -262,7 +291,7 @@ Add the following to your `~/.claude/settings.json`:
|
|
|
262
291
|
{
|
|
263
292
|
"statusLine": {
|
|
264
293
|
"type": "command",
|
|
265
|
-
"command": "BUN_BE_BUN=1 claude x cc-safety-net --
|
|
294
|
+
"command": "BUN_BE_BUN=1 claude x cc-safety-net statusline --claude-code"
|
|
266
295
|
}
|
|
267
296
|
}
|
|
268
297
|
```
|
|
@@ -277,20 +306,20 @@ Add the following to your `~/.claude/settings.json`:
|
|
|
277
306
|
{
|
|
278
307
|
"statusLine": {
|
|
279
308
|
"type": "command",
|
|
280
|
-
"command": "npx -y cc-safety-net --
|
|
309
|
+
"command": "npx -y cc-safety-net statusline --claude-code"
|
|
281
310
|
}
|
|
282
311
|
}
|
|
283
312
|
```
|
|
284
313
|
|
|
285
314
|
**Piping with existing status line:**
|
|
286
315
|
|
|
287
|
-
If you already have a status line command, you can pipe Safety Net at the end:
|
|
316
|
+
If you already have a status line command, you can pipe CC Safety Net at the end:
|
|
288
317
|
|
|
289
318
|
```json
|
|
290
319
|
{
|
|
291
320
|
"statusLine": {
|
|
292
321
|
"type": "command",
|
|
293
|
-
"command": "your-existing-command | bunx cc-safety-net --
|
|
322
|
+
"command": "your-existing-command | bunx cc-safety-net statusline --claude-code"
|
|
294
323
|
}
|
|
295
324
|
}
|
|
296
325
|
```
|
|
@@ -303,16 +332,16 @@ The status line displays different emojis based on the current configuration:
|
|
|
303
332
|
|
|
304
333
|
| Status | Display | Meaning |
|
|
305
334
|
|--------|---------|---------|
|
|
306
|
-
| Plugin disabled | `🛡️ Safety Net ❌` | Safety Net plugin is not enabled |
|
|
307
|
-
| Default mode | `🛡️ Safety Net ✅` | Protection active with default settings |
|
|
308
|
-
| Strict mode | `🛡️ Safety Net 🔒` | `
|
|
309
|
-
| Paranoid mode | `🛡️ Safety Net 👁️` | `
|
|
310
|
-
| Paranoid RM only | `🛡️ Safety Net 🗑️` | `
|
|
311
|
-
| Paranoid interpreters only | `🛡️ Safety Net 🐚` | `
|
|
312
|
-
| Worktree mode | `🛡️ Safety Net 🌳` | `
|
|
313
|
-
| Strict + Paranoid | `🛡️ Safety Net 🔒👁️` | Both strict and paranoid modes enabled |
|
|
335
|
+
| Plugin disabled | `🛡️ CC Safety Net ❌` | CC Safety Net plugin is not enabled |
|
|
336
|
+
| Default mode | `🛡️ CC Safety Net ✅` | Protection active with default settings |
|
|
337
|
+
| Strict mode | `🛡️ CC Safety Net 🔒` | `CC_SAFETY_NET_STRICT=1` — fail-closed on unparseable commands |
|
|
338
|
+
| Paranoid mode | `🛡️ CC Safety Net 👁️` | `CC_SAFETY_NET_PARANOID=1` — all paranoid checks enabled |
|
|
339
|
+
| Paranoid RM only | `🛡️ CC Safety Net 🗑️` | `CC_SAFETY_NET_PARANOID_RM=1` — blocks `rm -rf` even within cwd |
|
|
340
|
+
| Paranoid interpreters only | `🛡️ CC Safety Net 🐚` | `CC_SAFETY_NET_PARANOID_INTERPRETERS=1` — blocks interpreter one-liners |
|
|
341
|
+
| Worktree mode | `🛡️ CC Safety Net 🌳` | `CC_SAFETY_NET_WORKTREE=1` — relax local git discards inside linked worktrees |
|
|
342
|
+
| Strict + Paranoid | `🛡️ CC Safety Net 🔒👁️` | Both strict and paranoid modes enabled |
|
|
314
343
|
|
|
315
|
-
Multiple mode emojis are combined when multiple environment variables are set.
|
|
344
|
+
Multiple mode emojis are combined when multiple environment variables are set. Mode flags use `CC_SAFETY_NET_*` names; legacy `SAFETY_NET_*` names are still accepted.
|
|
316
345
|
|
|
317
346
|
## Diagnostics
|
|
318
347
|
|
|
@@ -331,7 +360,7 @@ The doctor command checks:
|
|
|
331
360
|
| Hook Integration | Verifies the plugin is properly configured for each supported platform |
|
|
332
361
|
| Self-Test | Runs sample commands to confirm blocking works correctly |
|
|
333
362
|
| Configuration | Validates custom rules in user and project configs |
|
|
334
|
-
| Environment | Shows status of mode flags (
|
|
363
|
+
| Environment | Shows status of mode flags (`CC_SAFETY_NET_STRICT`, `CC_SAFETY_NET_PARANOID`, etc.; legacy `SAFETY_NET_*` also listed when set) |
|
|
335
364
|
| Recent Activity | Summarizes blocked commands from the last 7 days |
|
|
336
365
|
| System Info | Displays versions of all relevant tools |
|
|
337
366
|
| Update Check | Checks if a newer version is available |
|
|
@@ -345,7 +374,7 @@ The doctor command checks:
|
|
|
345
374
|
|
|
346
375
|
## Explain (Debug Analysis)
|
|
347
376
|
|
|
348
|
-
Trace how Safety Net analyzes a command step-by-step. Useful for debugging why a command is blocked or allowed, or when developing custom rules.
|
|
377
|
+
Trace how CC Safety Net analyzes a command step-by-step. Useful for debugging why a command is blocked or allowed, or when developing custom rules.
|
|
349
378
|
|
|
350
379
|
```bash
|
|
351
380
|
npx cc-safety-net explain "git reset --hard"
|
|
@@ -377,6 +406,8 @@ npx cc-safety-net explain --cwd /tmp "git status"
|
|
|
377
406
|
| git checkout \<ref\> \<path\> | May overwrite working tree when Git disambiguates ref vs pathspec |
|
|
378
407
|
| git restore files | Discards uncommitted changes |
|
|
379
408
|
| git restore --worktree | Explicitly discards working tree changes |
|
|
409
|
+
| git switch --discard-changes | Discards uncommitted changes when switching branches |
|
|
410
|
+
| git switch --force / -f | Discards uncommitted changes (force switch) |
|
|
380
411
|
| git reset --hard | Destroys all uncommitted changes |
|
|
381
412
|
| git reset --merge | Can lose uncommitted changes |
|
|
382
413
|
| git clean -f | Removes untracked files permanently |
|
|
@@ -385,13 +416,16 @@ npx cc-safety-net explain --cwd /tmp "git status"
|
|
|
385
416
|
| git stash drop | Permanently deletes stashed changes |
|
|
386
417
|
| git stash clear | Deletes ALL stashed changes |
|
|
387
418
|
| git worktree remove --force | Force-deletes worktree without checking for changes |
|
|
388
|
-
| rm -rf (
|
|
419
|
+
| rm -rf (destructive targets) | Recursive file deletion of root, home, parent, absolute, or non-temp paths outside cwd |
|
|
389
420
|
| rm -rf / or ~ or $HOME | Root/home deletion is extremely dangerous |
|
|
390
421
|
| find ... -delete | Permanently removes files matching criteria |
|
|
391
422
|
| xargs rm -rf | Dynamic input makes targets unpredictable |
|
|
392
423
|
| xargs \<shell\> -c | Can execute arbitrary commands |
|
|
393
424
|
| parallel rm -rf | Dynamic input makes targets unpredictable |
|
|
394
425
|
| parallel \<shell\> -c | Can execute arbitrary commands |
|
|
426
|
+
| dd writing to block devices | Can overwrite disks or partitions |
|
|
427
|
+
| mkfs on block devices | Formats disks or partitions |
|
|
428
|
+
| shred | Permanently destroys file contents |
|
|
395
429
|
|
|
396
430
|
## Commands Allowed
|
|
397
431
|
|
|
@@ -408,7 +442,7 @@ npx cc-safety-net explain --cwd /tmp "git status"
|
|
|
408
442
|
| rm -rf /var/tmp/... | System temp directory |
|
|
409
443
|
| rm -rf $TMPDIR/... | User's temp directory |
|
|
410
444
|
| rm -rf ./... (within cwd) | Limited to current working directory |
|
|
411
|
-
| git restore / checkout -- / reset --hard / clean -f (in linked worktree) | Relaxed only when `
|
|
445
|
+
| git restore / checkout -- / reset --hard / clean -f (in linked worktree) | Relaxed only when `CC_SAFETY_NET_WORKTREE=1` and cwd is a linked worktree |
|
|
412
446
|
|
|
413
447
|
## What Happens When Blocked
|
|
414
448
|
|
|
@@ -416,7 +450,7 @@ When a destructive command is detected, the plugin blocks the tool execution and
|
|
|
416
450
|
|
|
417
451
|
Example output:
|
|
418
452
|
```text
|
|
419
|
-
BLOCKED by Safety Net
|
|
453
|
+
BLOCKED by CC Safety Net
|
|
420
454
|
|
|
421
455
|
Reason: git checkout -- discards uncommitted changes permanently. Use 'git stash' first.
|
|
422
456
|
|
|
@@ -437,29 +471,109 @@ git checkout -- README.md
|
|
|
437
471
|
git checkout -b test-branch
|
|
438
472
|
```
|
|
439
473
|
|
|
440
|
-
##
|
|
474
|
+
## Breaking Change: Custom Rules Migration
|
|
441
475
|
|
|
442
|
-
|
|
476
|
+
> [!WARNING]
|
|
477
|
+
> The custom rules system has moved from legacy inline config files to a rulebook-based layout. Legacy inline config files (`.safety-net.json` and `~/.cc-safety-net/config.json`) are **no longer loaded at runtime**. If they contain rules, commands now **fail closed** (stay blocked) until you migrate.
|
|
478
|
+
|
|
479
|
+
### Who Is Affected
|
|
480
|
+
|
|
481
|
+
- **Affected**: users who previously defined custom rules in `.safety-net.json` (project scope) or `~/.cc-safety-net/config.json` (user scope).
|
|
482
|
+
- **Not affected**: users with no custom rules. All built-in destructive-command protections are unchanged and continue to work out of the box.
|
|
483
|
+
|
|
484
|
+
### What Breaks
|
|
443
485
|
|
|
444
|
-
|
|
486
|
+
| Legacy file state | New behavior |
|
|
487
|
+
|-------------------|--------------|
|
|
488
|
+
| Empty legacy file | Silently ignored — built-in rules only |
|
|
489
|
+
| Legacy file with rules | Fail closed until migrated with `rule migrate` |
|
|
490
|
+
| Invalid legacy file | Fail closed until fixed and migrated, or removed |
|
|
491
|
+
|
|
492
|
+
"Fail closed" means commands stay blocked until the legacy rules are migrated to the new layout.
|
|
493
|
+
|
|
494
|
+
### How to Migrate
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
# Convert legacy inline rules into the new rulebook layout
|
|
498
|
+
npx -y cc-safety-net rule migrate
|
|
499
|
+
|
|
500
|
+
# Optionally delete verified legacy files after migration
|
|
501
|
+
npx -y cc-safety-net rule migrate --cleanup
|
|
502
|
+
|
|
503
|
+
# Validate the migrated rules
|
|
504
|
+
npx -y cc-safety-net rule verify
|
|
505
|
+
npx -y cc-safety-net rule test
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Before / After
|
|
509
|
+
|
|
510
|
+
**Before** — a single inline config with rules embedded:
|
|
511
|
+
|
|
512
|
+
```text
|
|
513
|
+
.safety-net.json # project rules (inline)
|
|
514
|
+
~/.cc-safety-net/config.json # user rules (inline)
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**After** — `rule migrate` creates a rulebook-based layout automatically:
|
|
518
|
+
|
|
519
|
+
```text
|
|
520
|
+
.cc-safety-net/rules/rule.json # project rulebook sources + overrides
|
|
521
|
+
.cc-safety-net/rules/project-rules/rulebook.json # migrated project rules
|
|
522
|
+
~/.cc-safety-net/rules/rule.json # user rulebook sources + overrides
|
|
523
|
+
~/.cc-safety-net/rules/user-rules/rulebook.json # migrated user rules
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
See [Custom Rules](#custom-rules) for the full authoring guide and [Error Handling](#error-handling) for fail-closed details.
|
|
527
|
+
|
|
528
|
+
## Custom Rules
|
|
445
529
|
|
|
446
530
|
Beyond the built-in protections, you can define your own blocking rules to enforce team conventions or project-specific safety policies.
|
|
447
531
|
|
|
448
532
|
> [!TIP]
|
|
449
|
-
>
|
|
450
|
-
|
|
533
|
+
> The best way to create custom rules is to use the `/cc-safety-net` skill to create custom rules interactively with natural language.
|
|
534
|
+
|
|
535
|
+
### Examples
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
/cc-safety-net read my package.json and suggest blocking rules
|
|
539
|
+
/cc-safety-net set up rules to block all terraform destroy commands
|
|
540
|
+
/cc-safety-net verify my rules and fix any errors
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
> [!NOTE]
|
|
451
544
|
> If your agent does not support skills, prompt it with:
|
|
452
|
-
> ```
|
|
453
|
-
> run npx cc-safety-net
|
|
545
|
+
> ```
|
|
546
|
+
> run npx -y cc-safety-net rule doc and help me set up custom rules
|
|
454
547
|
> ```
|
|
455
548
|
|
|
456
|
-
###
|
|
549
|
+
### Create Rules Manually
|
|
457
550
|
|
|
458
|
-
Create
|
|
551
|
+
Create a starter project rule config and rulebook:
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
npx -y cc-safety-net rule init
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
This creates `.cc-safety-net/rules/rule.json`:
|
|
459
558
|
|
|
460
559
|
```json
|
|
461
560
|
{
|
|
462
561
|
"version": 1,
|
|
562
|
+
"rules": ["project-rules"],
|
|
563
|
+
"overrides": {}
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
Rule definitions live in `.cc-safety-net/rules/project-rules/rulebook.json`:
|
|
568
|
+
|
|
569
|
+
```json
|
|
570
|
+
{
|
|
571
|
+
"rulebook_version": 1,
|
|
572
|
+
"name": "project-rules",
|
|
573
|
+
"version": "1.0.0",
|
|
574
|
+
"description": "Project-specific CC Safety Net rules.",
|
|
575
|
+
"author": "project",
|
|
576
|
+
"allowed_commands": ["git"],
|
|
463
577
|
"rules": [
|
|
464
578
|
{
|
|
465
579
|
"name": "block-git-add-all",
|
|
@@ -468,25 +582,48 @@ Create `.safety-net.json` in your project root:
|
|
|
468
582
|
"block_args": ["-A", "--all", "."],
|
|
469
583
|
"reason": "Use 'git add <specific-files>' instead of blanket add."
|
|
470
584
|
}
|
|
585
|
+
],
|
|
586
|
+
"tests": [
|
|
587
|
+
{
|
|
588
|
+
"command": "git add -A",
|
|
589
|
+
"expect": "blocked",
|
|
590
|
+
"rule": "block-git-add-all"
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"command": "git add README.md",
|
|
594
|
+
"expect": "allowed"
|
|
595
|
+
}
|
|
471
596
|
]
|
|
472
597
|
}
|
|
473
598
|
```
|
|
474
599
|
|
|
600
|
+
After editing rulebooks, run:
|
|
601
|
+
|
|
602
|
+
```bash
|
|
603
|
+
npx -y cc-safety-net rule sync
|
|
604
|
+
npx -y cc-safety-net rule verify
|
|
605
|
+
npx -y cc-safety-net rule test
|
|
606
|
+
```
|
|
607
|
+
|
|
475
608
|
Now `git add -A`, `git add --all`, and `git add .` will be blocked with your custom message.
|
|
476
609
|
|
|
477
610
|
### Config File Location
|
|
478
611
|
|
|
479
612
|
Config files are loaded from two scopes and merged:
|
|
480
613
|
|
|
481
|
-
1. **User scope**: `~/.cc-safety-net/
|
|
482
|
-
2. **Project scope**: `.safety-net.json` in the current working directory
|
|
614
|
+
1. **User scope**: `~/.cc-safety-net/rules/rule.json` (use `rule init --global`)
|
|
615
|
+
2. **Project scope**: `.cc-safety-net/rules/rule.json` in the current working directory
|
|
616
|
+
|
|
617
|
+
Local rulebook sources are bare names like `project-rules`. GitHub rulebook sources use `owner/repo#ref/<rulebook-name>` and point to `.cc-safety-net/rules/<rulebook-name>/rulebook.json` in that repository.
|
|
618
|
+
|
|
619
|
+
Legacy inline config files (`.safety-net.json` and `~/.cc-safety-net/config.json`) are no longer loaded at runtime. Empty legacy files are ignored, but legacy files with rules and invalid legacy files fail closed until migrated or fixed. Convert existing legacy rules with `npx -y cc-safety-net rule migrate`; use `npx -y cc-safety-net rule migrate --cleanup` if you also want to delete verified legacy files after migration. See [Breaking Change: Custom Rules Migration](#breaking-change-custom-rules-migration) for the full upgrade guide.
|
|
483
620
|
|
|
484
621
|
**Merging behavior**:
|
|
485
|
-
-
|
|
486
|
-
-
|
|
487
|
-
-
|
|
622
|
+
- Rulebooks from both scopes are combined
|
|
623
|
+
- Duplicate active rulebook names are invalid
|
|
624
|
+
- Project overrides win over user overrides for the same `<rulebook-name>/<rule-name>` key
|
|
488
625
|
|
|
489
|
-
This allows you to define personal defaults in user scope while letting projects
|
|
626
|
+
This allows you to define personal defaults in user scope while letting projects disable or replace reasons for specific rules.
|
|
490
627
|
|
|
491
628
|
If no config file is found in either location, only built-in rules apply.
|
|
492
629
|
|
|
@@ -495,18 +632,44 @@ If no config file is found in either location, only built-in rules apply.
|
|
|
495
632
|
| Field | Type | Required | Description |
|
|
496
633
|
|-------|------|----------|-------------|
|
|
497
634
|
| `version` | integer | Yes | Schema version (must be `1`) |
|
|
498
|
-
| `rules` | array | No | List of
|
|
635
|
+
| `rules` | array | No | List of rulebook source strings (defaults to empty) |
|
|
636
|
+
| `overrides` | object | No | Rule overrides keyed by `<rulebook-name>/<rule-name>` |
|
|
637
|
+
|
|
638
|
+
Override values are either `"off"` to disable a rule or `{ "reason": "..." }` to replace the rule reason.
|
|
639
|
+
|
|
640
|
+
### Rulebook Schema
|
|
641
|
+
|
|
642
|
+
| Field | Type | Required | Description |
|
|
643
|
+
|-------|------|----------|-------------|
|
|
644
|
+
| `rulebook_version` | integer | Yes | Rulebook schema version (must be `1`) |
|
|
645
|
+
| `name` | string | Yes | Rulebook name; must match the local directory name or GitHub source name |
|
|
646
|
+
| `version` | string | Yes | Rulebook version |
|
|
647
|
+
| `description` | string | No | Human-readable description |
|
|
648
|
+
| `author` | string | No | Rulebook author |
|
|
649
|
+
| `allowed_commands` | array | Yes | Commands this rulebook is allowed to define rules for |
|
|
650
|
+
| `rules` | array | Yes | Custom blocking rules |
|
|
651
|
+
| `tests` | array | Yes | Rulebook fixtures |
|
|
499
652
|
|
|
500
653
|
### Rule Schema
|
|
501
654
|
|
|
502
655
|
| Field | Type | Required | Description |
|
|
503
656
|
|-------|------|----------|-------------|
|
|
504
|
-
| `name` | string | Yes | Unique
|
|
505
|
-
| `command` | string | Yes | Base command to match
|
|
657
|
+
| `name` | string | Yes | Unique within the rulebook (letters, numbers, hyphens, underscores; max 64 chars) |
|
|
658
|
+
| `command` | string | Yes | Base command to match; must be listed in `allowed_commands` |
|
|
506
659
|
| `subcommand` | string | No | Subcommand to match (e.g., `add`, `install`). If omitted, matches any. |
|
|
507
660
|
| `block_args` | array | Yes | Arguments that trigger the block (at least one required) |
|
|
508
661
|
| `reason` | string | Yes | Message shown when blocked (max 256 chars) |
|
|
509
662
|
|
|
663
|
+
### Fixture Schema
|
|
664
|
+
|
|
665
|
+
| Field | Type | Required | Description |
|
|
666
|
+
|-------|------|----------|-------------|
|
|
667
|
+
| `command` | string | Yes | Shell command fixture |
|
|
668
|
+
| `expect` | string | Yes | Either `blocked` or `allowed` |
|
|
669
|
+
| `rule` | string | For blocked fixtures | Rule expected to block the command |
|
|
670
|
+
|
|
671
|
+
Every rule must have at least one blocked fixture. Add allowed fixtures for close-but-safe commands.
|
|
672
|
+
|
|
510
673
|
### Matching Behavior
|
|
511
674
|
|
|
512
675
|
- **Commands** are normalized to basename (`/usr/bin/git` → `git`)
|
|
@@ -521,13 +684,28 @@ If no config file is found in either location, only built-in rules apply.
|
|
|
521
684
|
|
|
522
685
|
- **Short option expansion**: `-Cfoo` is treated as `-C -f -o -o`, not `-C foo`. Blocking `-f` may false-positive on attached option values.
|
|
523
686
|
|
|
524
|
-
### Examples
|
|
687
|
+
### Rule Examples
|
|
525
688
|
|
|
526
689
|
#### Block global npm installs
|
|
527
690
|
|
|
691
|
+
`.cc-safety-net/rules/rule.json`:
|
|
692
|
+
|
|
528
693
|
```json
|
|
529
694
|
{
|
|
530
695
|
"version": 1,
|
|
696
|
+
"rules": ["project-rules"],
|
|
697
|
+
"overrides": {}
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
`.cc-safety-net/rules/project-rules/rulebook.json`:
|
|
702
|
+
|
|
703
|
+
```json
|
|
704
|
+
{
|
|
705
|
+
"rulebook_version": 1,
|
|
706
|
+
"name": "project-rules",
|
|
707
|
+
"version": "1.0.0",
|
|
708
|
+
"allowed_commands": ["npm"],
|
|
531
709
|
"rules": [
|
|
532
710
|
{
|
|
533
711
|
"name": "block-npm-global",
|
|
@@ -536,6 +714,17 @@ If no config file is found in either location, only built-in rules apply.
|
|
|
536
714
|
"block_args": ["-g", "--global"],
|
|
537
715
|
"reason": "Global npm installs can cause version conflicts. Use npx or local install."
|
|
538
716
|
}
|
|
717
|
+
],
|
|
718
|
+
"tests": [
|
|
719
|
+
{
|
|
720
|
+
"command": "npm install -g typescript",
|
|
721
|
+
"expect": "blocked",
|
|
722
|
+
"rule": "block-npm-global"
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
"command": "npm install typescript",
|
|
726
|
+
"expect": "allowed"
|
|
727
|
+
}
|
|
539
728
|
]
|
|
540
729
|
}
|
|
541
730
|
```
|
|
@@ -544,7 +733,10 @@ If no config file is found in either location, only built-in rules apply.
|
|
|
544
733
|
|
|
545
734
|
```json
|
|
546
735
|
{
|
|
547
|
-
"
|
|
736
|
+
"rulebook_version": 1,
|
|
737
|
+
"name": "project-rules",
|
|
738
|
+
"version": "1.0.0",
|
|
739
|
+
"allowed_commands": ["docker"],
|
|
548
740
|
"rules": [
|
|
549
741
|
{
|
|
550
742
|
"name": "block-docker-system-prune",
|
|
@@ -553,6 +745,17 @@ If no config file is found in either location, only built-in rules apply.
|
|
|
553
745
|
"block_args": ["prune"],
|
|
554
746
|
"reason": "docker system prune removes all unused data. Use targeted cleanup instead."
|
|
555
747
|
}
|
|
748
|
+
],
|
|
749
|
+
"tests": [
|
|
750
|
+
{
|
|
751
|
+
"command": "docker system prune",
|
|
752
|
+
"expect": "blocked",
|
|
753
|
+
"rule": "block-docker-system-prune"
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
"command": "docker ps",
|
|
757
|
+
"expect": "allowed"
|
|
758
|
+
}
|
|
556
759
|
]
|
|
557
760
|
}
|
|
558
761
|
```
|
|
@@ -561,7 +764,10 @@ If no config file is found in either location, only built-in rules apply.
|
|
|
561
764
|
|
|
562
765
|
```json
|
|
563
766
|
{
|
|
564
|
-
"
|
|
767
|
+
"rulebook_version": 1,
|
|
768
|
+
"name": "project-rules",
|
|
769
|
+
"version": "1.0.0",
|
|
770
|
+
"allowed_commands": ["git", "npm"],
|
|
565
771
|
"rules": [
|
|
566
772
|
{
|
|
567
773
|
"name": "block-git-add-all",
|
|
@@ -577,33 +783,47 @@ If no config file is found in either location, only built-in rules apply.
|
|
|
577
783
|
"block_args": ["-g", "--global"],
|
|
578
784
|
"reason": "Use npx or local install instead of global."
|
|
579
785
|
}
|
|
786
|
+
],
|
|
787
|
+
"tests": [
|
|
788
|
+
{
|
|
789
|
+
"command": "git add -A",
|
|
790
|
+
"expect": "blocked",
|
|
791
|
+
"rule": "block-git-add-all"
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
"command": "npm install -g typescript",
|
|
795
|
+
"expect": "blocked",
|
|
796
|
+
"rule": "block-npm-global"
|
|
797
|
+
}
|
|
580
798
|
]
|
|
581
799
|
}
|
|
582
800
|
```
|
|
583
801
|
|
|
584
802
|
### Error Handling
|
|
585
803
|
|
|
586
|
-
|
|
804
|
+
Rulebook-backed custom rules fail closed when configured rulebooks cannot be loaded safely:
|
|
587
805
|
|
|
588
806
|
| Scenario | Behavior |
|
|
589
807
|
|----------|----------|
|
|
590
808
|
| Config file not found | Silent — use built-in rules only |
|
|
591
|
-
|
|
|
592
|
-
|
|
|
593
|
-
|
|
|
594
|
-
| Invalid
|
|
595
|
-
|
|
|
809
|
+
| Invalid rule config | Fail closed until fixed |
|
|
810
|
+
| Empty legacy config | Silent — use built-in rules only |
|
|
811
|
+
| Legacy config with rules and no migrated rule config | Fail closed until `rule migrate` creates the new rule config |
|
|
812
|
+
| Invalid legacy config | Fail closed until fixed or removed |
|
|
813
|
+
| Missing or stale lock/cache | Fail closed until `rule sync` repairs it |
|
|
814
|
+
| Invalid local rulebook | Fail closed until the rulebook is fixed and synced |
|
|
815
|
+
| Invalid GitHub rulebook | Fail closed until the source is fixed or removed |
|
|
596
816
|
|
|
597
817
|
|
|
598
818
|
> [!IMPORTANT]
|
|
599
|
-
> If you add or modify custom rules manually, always validate them with `npx -y cc-safety-net
|
|
819
|
+
> If you add or modify custom rules manually, always validate them with `npx -y cc-safety-net rule verify` and `npx -y cc-safety-net rule test`.
|
|
600
820
|
|
|
601
821
|
### Block Output Format
|
|
602
822
|
|
|
603
823
|
When a custom rule blocks a command, the output includes the rule name:
|
|
604
824
|
|
|
605
825
|
```text
|
|
606
|
-
BLOCKED by Safety Net
|
|
826
|
+
BLOCKED by CC Safety Net
|
|
607
827
|
|
|
608
828
|
Reason: [block-git-add-all] Use 'git add <specific-files>' instead of blanket add.
|
|
609
829
|
|
|
@@ -612,14 +832,17 @@ Command: git add -A
|
|
|
612
832
|
|
|
613
833
|
## Advanced Features
|
|
614
834
|
|
|
835
|
+
Mode and debug flags use **`CC_SAFETY_NET_*`** environment variables. Older **`SAFETY_NET_*`** names (without the `CC_` prefix) still work for strict, paranoid, and worktree toggles.
|
|
836
|
+
|
|
615
837
|
### Strict Mode
|
|
616
838
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
unterminated quotes
|
|
839
|
+
Malformed or missing hook input JSON always fails closed. By default, ambiguous shell
|
|
840
|
+
command parsing is allowed through. Enable strict mode to fail closed when a shell
|
|
841
|
+
command cannot be safely analyzed (e.g., unterminated quotes or malformed `bash -c`
|
|
842
|
+
wrappers):
|
|
620
843
|
|
|
621
844
|
```bash
|
|
622
|
-
export
|
|
845
|
+
export CC_SAFETY_NET_STRICT=1
|
|
623
846
|
```
|
|
624
847
|
|
|
625
848
|
### Paranoid Mode
|
|
@@ -629,11 +852,11 @@ You can enable it globally or via focused toggles:
|
|
|
629
852
|
|
|
630
853
|
```bash
|
|
631
854
|
# Enable all paranoid checks
|
|
632
|
-
export
|
|
855
|
+
export CC_SAFETY_NET_PARANOID=1
|
|
633
856
|
|
|
634
857
|
# Or enable specific paranoid checks
|
|
635
|
-
export
|
|
636
|
-
export
|
|
858
|
+
export CC_SAFETY_NET_PARANOID_RM=1
|
|
859
|
+
export CC_SAFETY_NET_PARANOID_INTERPRETERS=1
|
|
637
860
|
```
|
|
638
861
|
|
|
639
862
|
Paranoid behavior:
|
|
@@ -650,7 +873,7 @@ local-discard rules when (and only when) the command is proven to run inside a
|
|
|
650
873
|
linked worktree:
|
|
651
874
|
|
|
652
875
|
```bash
|
|
653
|
-
export
|
|
876
|
+
export CC_SAFETY_NET_WORKTREE=1
|
|
654
877
|
```
|
|
655
878
|
|
|
656
879
|
When enabled, these commands are allowed inside a linked worktree:
|
|
@@ -691,6 +914,7 @@ The guard recursively analyzes commands wrapped in shells:
|
|
|
691
914
|
```bash
|
|
692
915
|
bash -c 'git reset --hard' # Blocked
|
|
693
916
|
sh -lc 'rm -rf /' # Blocked
|
|
917
|
+
bash -c 'git stash drop' # Blocked
|
|
694
918
|
```
|
|
695
919
|
|
|
696
920
|
### Interpreter One-Liner Detection
|
|
@@ -699,6 +923,10 @@ Detects destructive commands hidden in Python/Node/Ruby/Perl one-liners:
|
|
|
699
923
|
|
|
700
924
|
```bash
|
|
701
925
|
python -c 'import os; os.system("rm -rf /")' # Blocked
|
|
926
|
+
python -c 'import os; os.system("git stash drop")' # Blocked
|
|
927
|
+
python -c 'import os; os.system("dd if=/dev/zero of=/dev/sda")' # Blocked
|
|
928
|
+
python -c 'import os; os.system("mkfs.ext4 /dev/sda1")' # Blocked
|
|
929
|
+
python -c 'import os; os.system("shred -u secret.txt")' # Blocked
|
|
702
930
|
```
|
|
703
931
|
|
|
704
932
|
### Secret Redaction
|
|
@@ -715,6 +943,10 @@ All blocked commands are logged to `~/.cc-safety-net/logs/<session_id>.jsonl` fo
|
|
|
715
943
|
|
|
716
944
|
Sensitive data in log entries is automatically redacted.
|
|
717
945
|
|
|
946
|
+
## Development
|
|
947
|
+
|
|
948
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to this project.
|
|
949
|
+
|
|
718
950
|
## License
|
|
719
951
|
|
|
720
952
|
MIT
|