@sureshsankaran/destructive-check-plugin 1.0.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/package.json +44 -0
  4. package/src/index.ts +494 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenCode
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # @opencode-ai/destructive-check-plugin
2
+
3
+ OpenCode plugin that automatically detects destructive commands and requires user permission before execution.
4
+
5
+ ## Features
6
+
7
+ This plugin protects against potentially harmful operations by detecting:
8
+
9
+ - **File deletion**: `rm -rf`, `rm -fr` with dangerous paths (/, /home, /etc, etc.)
10
+ - **Git operations**: `git push --force`, `git reset --hard`, `git clean -f`
11
+ - **Database commands**: `DROP TABLE`, `DELETE FROM`, `TRUNCATE`
12
+ - **System operations**: `chmod 777`, `dd`, `mkfs`, `format`
13
+ - **Elevated privileges**: `sudo rm`, `sudo chmod`, `sudo dd`
14
+ - **Container/Cloud**: `kubectl delete`, `docker rm -f`, `aws s3 rm --recursive`
15
+ - **Package managers**: `npm cache clean --force`, `pip uninstall -y`
16
+ - **Network**: `iptables -F`, `ufw reset`
17
+
18
+ ## Installation
19
+
20
+ ### 1. Install the package
21
+
22
+ ```bash
23
+ bun add @opencode-ai/destructive-check-plugin
24
+ ```
25
+
26
+ ### 2. Enable in your OpenCode configuration
27
+
28
+ Add to your `opencode.json` or `.opencode/opencode.json`:
29
+
30
+ ```json
31
+ {
32
+ "plugin": ["@opencode-ai/destructive-check-plugin"]
33
+ }
34
+ ```
35
+
36
+ Or use a file path:
37
+
38
+ ```json
39
+ {
40
+ "plugin": ["file:///path/to/node_modules/@opencode-ai/destructive-check-plugin/dist/index.js"]
41
+ }
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ Once installed, the plugin automatically:
47
+
48
+ 1. **Monitors** all tool calls and bash commands
49
+ 2. **Detects** destructive patterns using regex matching
50
+ 3. **Warns** in the console when a destructive command is detected
51
+ 4. **Requests permission** from the user before executing dangerous operations
52
+
53
+ ### Severity Levels
54
+
55
+ - **🔴 CRITICAL**: File deletion on system paths, sudo operations, system-level changes
56
+ - **🟠 HIGH**: Git history rewriting, database modifications, container deletions
57
+ - **🟡 MEDIUM**: Package manager operations, network configuration changes
58
+
59
+ ### Example Output
60
+
61
+ ```
62
+ [destructive-check] 🔴 CRITICAL destructive command detected
63
+ Category: Dangerous File Deletion
64
+ Command: rm -rf /
65
+ ⚠️ This operation could cause data loss or system damage!
66
+ ```
67
+
68
+ ## Check Plugin Status
69
+
70
+ Use the built-in tool to check plugin status:
71
+
72
+ ```typescript
73
+ // In OpenCode
74
+ destructive - check - status
75
+ ```
76
+
77
+ Returns:
78
+
79
+ ```json
80
+ {
81
+ "enabled": true,
82
+ "session": {
83
+ "id": "session-123",
84
+ "checked": 42,
85
+ "permissionsRequested": 3
86
+ },
87
+ "global": {
88
+ "checked": 156,
89
+ "permissionsRequested": 12
90
+ },
91
+ "patterns": {
92
+ "categories": ["rmDangerous", "git", "database", ...],
93
+ "total": 45
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## Detected Patterns
99
+
100
+ ### File Deletion
101
+
102
+ - `rm /`, `rm -rf /home`, `rm -rf /etc`
103
+ - `rm .git`, `rm package.json`
104
+ - `rm $HOME`, `rm ~/*`
105
+
106
+ ### Git Operations
107
+
108
+ - `git push --force`, `git push -f`
109
+ - `git reset --hard`, `git reset HEAD~1`
110
+ - `git clean -f`, `git stash drop`
111
+ - `git branch -D`
112
+
113
+ ### Database
114
+
115
+ - `DROP TABLE`, `DROP DATABASE`
116
+ - `DELETE FROM table` (without WHERE)
117
+ - `TRUNCATE TABLE`
118
+
119
+ ### System
120
+
121
+ - `chmod 777 /`, `chown user /`
122
+ - `dd of=/dev/sda`
123
+ - `mkfs`, `format`, `fdisk`
124
+
125
+ ### Containers/Cloud
126
+
127
+ - `kubectl delete namespace`
128
+ - `docker rm -f`, `docker system prune -a`
129
+ - `aws ec2 terminate-instances`
130
+ - `gcloud compute instances delete`
131
+
132
+ ## Development
133
+
134
+ ### Build
135
+
136
+ ```bash
137
+ bun run build
138
+ ```
139
+
140
+ ### Test Locally
141
+
142
+ Link the package locally:
143
+
144
+ ```bash
145
+ cd packages/destructive-check-plugin
146
+ bun link
147
+ cd /your/opencode/project
148
+ bun link @opencode-ai/destructive-check-plugin
149
+ ```
150
+
151
+ Add to your `opencode.json`:
152
+
153
+ ```json
154
+ {
155
+ "plugin": ["@opencode-ai/destructive-check-plugin"]
156
+ }
157
+ ```
158
+
159
+ ## How It Works
160
+
161
+ The plugin uses three hooks:
162
+
163
+ 1. **`tool.execute.before`**: Logs warnings when destructive commands are detected
164
+ 2. **`permission.ask`**: Intercepts permission requests and flags destructive operations
165
+ 3. **`tool.execute.after`**: Logs completion and any system blocks
166
+
167
+ ## License
168
+
169
+ MIT
170
+
171
+ ## Contributing
172
+
173
+ Issues and pull requests are welcome at [github.com/anomalyco/opencode](https://github.com/anomalyco/opencode).
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@sureshsankaran/destructive-check-plugin",
3
+ "version": "1.0.0",
4
+ "description": "OpenCode plugin that detects and requires user permission for destructive commands before execution",
5
+ "main": "src/index.ts",
6
+ "types": "src/index.ts",
7
+ "type": "module",
8
+ "files": [
9
+ "src",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
13
+ "scripts": {},
14
+ "keywords": [
15
+ "opencode",
16
+ "opencode-plugin",
17
+ "destructive-commands",
18
+ "safety",
19
+ "security",
20
+ "git",
21
+ "rm",
22
+ "database",
23
+ "docker",
24
+ "kubernetes"
25
+ ],
26
+ "author": "OpenCode",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/anomalyco/opencode.git",
31
+ "directory": "packages/destructive-check-plugin"
32
+ },
33
+ "homepage": "https://github.com/anomalyco/opencode/tree/main/packages/destructive-check-plugin#readme",
34
+ "bugs": {
35
+ "url": "https://github.com/anomalyco/opencode/issues"
36
+ },
37
+ "peerDependencies": {
38
+ "@opencode-ai/plugin": "*"
39
+ },
40
+ "devDependencies": {},
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ }
44
+ }
package/src/index.ts ADDED
@@ -0,0 +1,494 @@
1
+ /**
2
+ * Destructive Command Check Plugin for OpenCode
3
+ *
4
+ * Automatically checks for destructive commands before any tool/bash call.
5
+ * This plugin runs for all sessions and agents, protecting against potentially
6
+ * harmful operations by asking for user permission.
7
+ *
8
+ * Destructive patterns detected:
9
+ * - rm -rf, rm -fr, rm -r with dangerous paths
10
+ * - git push --force, git reset (--hard/--soft/--mixed) moving HEAD
11
+ * - DROP TABLE, DELETE FROM, TRUNCATE
12
+ * - chmod 777, chown on system dirs
13
+ * - dd commands
14
+ * - format/mkfs commands
15
+ * - sudo rm, sudo chmod, sudo chown
16
+ * - kubectl delete, docker rm -f
17
+ * - aws s3 rm --recursive
18
+ *
19
+ * The plugin will:
20
+ * 1. Detect destructive patterns in bash commands
21
+ * 2. Detect destructive file operations (deleting important files)
22
+ * 3. Ask for user permission before executing dangerous operations
23
+ *
24
+ * Installation:
25
+ * Add to your opencode.json config:
26
+ * {
27
+ * "plugin": ["file:///path/to/.opencode/plugins/destructive-check.ts"]
28
+ * }
29
+ */
30
+
31
+ import type { Plugin } from "@opencode-ai/plugin"
32
+
33
+ // Destructive command patterns to check
34
+ const DESTRUCTIVE_PATTERNS = {
35
+ // File deletion - dangerous patterns
36
+ rmDangerous: [
37
+ /\brm\s+(-[rRf]+\s+)*[\/~]\s*$/i, // rm / or rm ~
38
+ /\brm\s+(-[rRf]+\s+)*\/\*/, // rm /*
39
+ /\brm\s+(-[rRf]+\s+)*~\/\*/, // rm ~/*
40
+ /\brm\s+(-[rRf]+\s+)*\$HOME\b/i, // rm $HOME
41
+ /\brm\s+(-[rRf]+\s+)*\/home\b/i, // rm /home
42
+ /\brm\s+(-[rRf]+\s+)*\/etc\b/i, // rm /etc
43
+ /\brm\s+(-[rRf]+\s+)*\/var\b/i, // rm /var
44
+ /\brm\s+(-[rRf]+\s+)*\/usr\b/i, // rm /usr
45
+ /\brm\s+(-[rRf]+\s+)*\/bin\b/i, // rm /bin
46
+ /\brm\s+(-[rRf]+\s+)*\/sbin\b/i, // rm /sbin
47
+ /\brm\s+(-[rRf]+\s+)*\/boot\b/i, // rm /boot
48
+ /\brm\s+(-[rRf]+\s+)*\/lib\b/i, // rm /lib
49
+ /\brm\s+(-[rRf]+\s+)*\/opt\b/i, // rm /opt
50
+ /\brm\s+(-[rRf]+\s+)*\/root\b/i, // rm /root
51
+ /\brm\s+(-[rRf]+\s+)*\/sys\b/i, // rm /sys
52
+ /\brm\s+(-[rRf]+\s+)*\/proc\b/i, // rm /proc
53
+ /\brm\s+(-[rRf]+\s+)*\/dev\b/i, // rm /dev
54
+ /\brm\s+(-[rRf]+\s+)*\/mnt\b/i, // rm /mnt
55
+ /\brm\s+(-[rRf]+\s+)*\/tmp\b/i, // rm /tmp
56
+ /\brm\s+(-[rRf]+\s+)*\.git\b/i, // rm .git
57
+ /\brm\s+(-[rRf]+\s+)*node_modules\b/i, // rm node_modules (dangerous in wrong dir)
58
+ ],
59
+
60
+ // Git destructive operations
61
+ git: [
62
+ /\bgit\s+push\s+.*--force\b/i, // git push --force
63
+ /\bgit\s+push\s+.*-f\b/i, // git push -f
64
+ // git reset patterns: catch both HEAD movement (rewrites history) and --hard (discards changes)
65
+ /\bgit\s+reset\s+(--hard|--soft|--mixed)?\s*(HEAD|@)[\~\^]/i, // git reset moving HEAD (rewrites commit history)
66
+ /\bgit\s+reset\s+--hard\b/i, // git reset --hard (discards working directory changes)
67
+ /\bgit\s+clean\s+.*-f/i, // git clean -f
68
+ /\bgit\s+checkout\s+--\s+\./i, // git checkout -- .
69
+ /\bgit\s+stash\s+drop/i, // git stash drop
70
+ /\bgit\s+branch\s+.*-D\b/i, // git branch -D
71
+ /\bgit\s+reflog\s+expire/i, // git reflog expire
72
+ /\bgit\s+gc\s+--prune/i, // git gc --prune
73
+ ],
74
+
75
+ // Database destructive operations
76
+ database: [
77
+ /\bDROP\s+(TABLE|DATABASE|SCHEMA|INDEX)\b/i,
78
+ /\bTRUNCATE\s+TABLE\b/i,
79
+ /\bDELETE\s+FROM\s+\S+\s*(;|\s*$)/i, // DELETE without WHERE
80
+ /\bALTER\s+TABLE\s+\S+\s+DROP\b/i,
81
+ ],
82
+
83
+ // System destructive operations
84
+ system: [
85
+ /\bchmod\s+(-R\s+)?777\s+\//i, // chmod 777 /
86
+ /\bchown\s+(-R\s+)?\S+\s+\//i, // chown on root
87
+ /\bdd\s+.*of=\/dev\//i, // dd to device
88
+ /\bmkfs\b/i, // Format filesystem
89
+ /\bformat\s+[a-z]:/i, // Windows format
90
+ /\bfdisk\b/i, // Partition tool
91
+ /\bparted\b/i, // Partition tool
92
+ ],
93
+
94
+ // Elevated privileges with destructive commands
95
+ sudo: [
96
+ /\bsudo\s+rm\s+(-[rRf]+\s+)*\//i, // sudo rm on root
97
+ /\bsudo\s+chmod\b/i, // sudo chmod
98
+ /\bsudo\s+chown\b/i, // sudo chown
99
+ /\bsudo\s+dd\b/i, // sudo dd
100
+ /\bsudo\s+mkfs\b/i, // sudo mkfs
101
+ ],
102
+
103
+ // Container/cloud destructive operations
104
+ container: [
105
+ /\bkubectl\s+delete\s+(namespace|ns|pod|deployment|service)\b/i,
106
+ /\bdocker\s+rm\s+.*-f/i, // docker rm -f
107
+ /\bdocker\s+system\s+prune\s+.*-a/i, // docker system prune -a
108
+ /\bdocker\s+volume\s+rm\b/i, // docker volume rm
109
+ /\baws\s+s3\s+rm\s+.*--recursive\b/i, // aws s3 rm --recursive
110
+ /\baws\s+ec2\s+terminate-instances\b/i, // terminate EC2
111
+ /\bgcloud\s+.*delete\b/i, // gcloud delete operations
112
+ ],
113
+
114
+ // Package manager destructive operations
115
+ packages: [
116
+ /\bnpm\s+cache\s+clean\s+--force\b/i, // npm cache clean --force
117
+ /\byarn\s+cache\s+clean\b/i, // yarn cache clean
118
+ /\bpip\s+uninstall\s+.*-y\b/i, // pip uninstall -y (auto-confirm)
119
+ /\bbrew\s+uninstall\s+--force\b/i, // brew uninstall --force
120
+ ],
121
+
122
+ // Network destructive operations
123
+ network: [
124
+ /\biptables\s+.*-F\b/i, // Flush iptables
125
+ /\biptables\s+.*--flush\b/i, // Flush iptables
126
+ /\bufw\s+reset\b/i, // Reset firewall
127
+ ],
128
+ }
129
+
130
+ // File paths that are dangerous to delete/modify
131
+ const DANGEROUS_PATHS = [
132
+ "/",
133
+ "/*",
134
+ "/home",
135
+ "/etc",
136
+ "/var",
137
+ "/usr",
138
+ "/bin",
139
+ "/sbin",
140
+ "/boot",
141
+ "/lib",
142
+ "/opt",
143
+ "/root",
144
+ "/sys",
145
+ "/proc",
146
+ "/dev",
147
+ "~",
148
+ "~/",
149
+ "$HOME",
150
+ ".git",
151
+ ".env",
152
+ ".ssh",
153
+ "package.json",
154
+ "package-lock.json",
155
+ "yarn.lock",
156
+ "bun.lockb",
157
+ "Cargo.toml",
158
+ "go.mod",
159
+ "pyproject.toml",
160
+ "requirements.txt",
161
+ ]
162
+
163
+ type DestructiveMatch = {
164
+ category: string
165
+ pattern: string
166
+ severity: "critical" | "high" | "medium"
167
+ command: string
168
+ }
169
+
170
+ // Check if a command matches any destructive pattern
171
+ function checkCommand(command: string): DestructiveMatch | null {
172
+ for (const [category, patterns] of Object.entries(DESTRUCTIVE_PATTERNS)) {
173
+ for (const pattern of patterns) {
174
+ if (pattern.test(command)) {
175
+ const severity = getSeverity(category)
176
+ return {
177
+ category,
178
+ pattern: pattern.toString(),
179
+ severity,
180
+ command,
181
+ }
182
+ }
183
+ }
184
+ }
185
+ return null
186
+ }
187
+
188
+ // Determine severity based on category
189
+ function getSeverity(category: string): "critical" | "high" | "medium" {
190
+ if (category === "rmDangerous" || category === "sudo" || category === "system") {
191
+ return "critical"
192
+ }
193
+ if (category === "git" || category === "database" || category === "container") {
194
+ return "high"
195
+ }
196
+ return "medium"
197
+ }
198
+
199
+ // Get human-readable label for category
200
+ function getCategoryLabel(category: string): string {
201
+ const labels: Record<string, string> = {
202
+ rmDangerous: "Dangerous File Deletion",
203
+ git: "Destructive Git Operation",
204
+ database: "Database Modification",
205
+ system: "System-Level Change",
206
+ sudo: "Elevated Privilege Operation",
207
+ container: "Container/Cloud Operation",
208
+ packages: "Package Management",
209
+ network: "Network Configuration",
210
+ }
211
+ return labels[category] || category
212
+ }
213
+
214
+ // Check if a file path is dangerous
215
+ function isDangerousPath(path: string): boolean {
216
+ const normalized = path.replace(/\\/g, "/").toLowerCase()
217
+ return DANGEROUS_PATHS.some((dangerous) => {
218
+ const normalizedDangerous = dangerous.toLowerCase()
219
+ return (
220
+ normalized === normalizedDangerous ||
221
+ normalized.startsWith(normalizedDangerous + "/") ||
222
+ normalized.endsWith("/" + normalizedDangerous)
223
+ )
224
+ })
225
+ }
226
+
227
+ // Statistics tracking
228
+ type Stats = {
229
+ checked: number
230
+ permissionsRequested: number
231
+ lastMatch?: DestructiveMatch
232
+ }
233
+
234
+ // Plugin state per session
235
+ const sessions: Record<string, Stats> = {}
236
+
237
+ function getStats(sessionID: string): Stats {
238
+ if (!sessions[sessionID]) {
239
+ sessions[sessionID] = { checked: 0, permissionsRequested: 0 }
240
+ }
241
+ return sessions[sessionID]
242
+ }
243
+
244
+ // Global stats
245
+ const global: Stats = { checked: 0, permissionsRequested: 0 }
246
+
247
+ /**
248
+ * Destructive Command Check Plugin
249
+ */
250
+ async function destructiveCheck(_input: {
251
+ client: any
252
+ project: any
253
+ worktree: string
254
+ directory: string
255
+ serverUrl: any
256
+ $: any
257
+ }) {
258
+ return {
259
+ // Tool for checking plugin status
260
+ tool: {
261
+ "destructive-check-status": {
262
+ description: "Get the status of the destructive command check plugin",
263
+ args: {},
264
+ async execute(_args: {}, ctx: { sessionID: string }) {
265
+ const stats = getStats(ctx.sessionID)
266
+ return JSON.stringify(
267
+ {
268
+ enabled: true,
269
+ session: {
270
+ id: ctx.sessionID,
271
+ ...stats,
272
+ },
273
+ global: global,
274
+ patterns: {
275
+ categories: Object.keys(DESTRUCTIVE_PATTERNS),
276
+ total: Object.values(DESTRUCTIVE_PATTERNS).flat().length,
277
+ },
278
+ dangerousPaths: DANGEROUS_PATHS.length,
279
+ },
280
+ null,
281
+ 2,
282
+ )
283
+ },
284
+ },
285
+ },
286
+
287
+ // Check before any tool executes - logs warnings for awareness
288
+ async ["tool.execute.before"](
289
+ hookInput: { tool: string; sessionID: string; callID: string },
290
+ output: { args: Record<string, unknown> },
291
+ ): Promise<void> {
292
+ const stats = getStats(hookInput.sessionID)
293
+ stats.checked++
294
+ global.checked++
295
+
296
+ const tool = hookInput.tool.toLowerCase()
297
+ const args = output.args
298
+
299
+ // Check bash/shell commands
300
+ if (tool === "bash" || tool === "shell" || tool === "execute") {
301
+ const command = (args?.command as string) || ""
302
+ if (command) {
303
+ const match = checkCommand(command)
304
+ if (match) {
305
+ console.warn(
306
+ `[destructive-check] Detected ${match.severity.toUpperCase()} destructive command - permission will be requested`,
307
+ )
308
+ console.warn(` Category: ${match.category}`)
309
+ console.warn(` Command: ${match.command.slice(0, 100)}${match.command.length > 100 ? "..." : ""}`)
310
+ }
311
+ }
312
+ }
313
+
314
+ // Check file write/delete operations
315
+ if (tool === "write" || tool === "edit" || tool === "delete" || tool === "remove") {
316
+ const filePath = (args?.filePath as string) || (args?.path as string) || ""
317
+ if (filePath && isDangerousPath(filePath)) {
318
+ console.warn(`[destructive-check] Dangerous file operation detected - permission will be requested`)
319
+ console.warn(` Tool: ${tool}`)
320
+ console.warn(` Path: ${filePath}`)
321
+ }
322
+ }
323
+
324
+ // Check git operations via tool
325
+ if (tool === "git") {
326
+ const subcommand = (args?.subcommand as string) || (args?.command as string) || ""
327
+ const fullCommand = `git ${subcommand}`
328
+ const match = checkCommand(fullCommand)
329
+ if (match) {
330
+ console.warn(`[destructive-check] Destructive git operation detected - permission will be requested`)
331
+ console.warn(` Severity: ${match.severity.toUpperCase()}`)
332
+ console.warn(` Command: ${fullCommand}`)
333
+ }
334
+ }
335
+ },
336
+
337
+ // Permission hook to require user confirmation for destructive operations
338
+ async ["permission.ask"](
339
+ input: {
340
+ id: string
341
+ // Old permission system uses 'type', new system uses 'permission'
342
+ type?: string
343
+ permission?: string
344
+ pattern?: string | string[]
345
+ patterns?: string[]
346
+ sessionID: string
347
+ messageID?: string
348
+ callID?: string
349
+ title?: string
350
+ metadata: Record<string, unknown>
351
+ time?: { created: number }
352
+ },
353
+ output: { status: "ask" | "deny" | "allow"; metadata?: Record<string, unknown> },
354
+ ): Promise<void> {
355
+ const stats = getStats(input.sessionID)
356
+
357
+ // Get permission type from either old or new system
358
+ const permissionType = input.permission || input.type || ""
359
+
360
+ // Check if this is a bash/command execution permission
361
+ if (permissionType === "bash" || permissionType === "command" || permissionType === "shell") {
362
+ // Get commands from patterns (new system) or metadata/title (old system)
363
+ const patterns =
364
+ input.patterns || (Array.isArray(input.pattern) ? input.pattern : input.pattern ? [input.pattern] : [])
365
+
366
+ // Check each pattern individually for destructive commands
367
+ for (const pattern of patterns) {
368
+ const match = checkCommand(pattern)
369
+ if (match) {
370
+ stats.permissionsRequested++
371
+ global.permissionsRequested++
372
+ stats.lastMatch = match
373
+
374
+ // Add metadata to the permission request for UI display
375
+ const severityEmoji = match.severity === "critical" ? "🔴" : match.severity === "high" ? "🟠" : "🟡"
376
+ const categoryLabel = getCategoryLabel(match.category)
377
+
378
+ // Enhance metadata with destructive command information
379
+ if (!output.metadata) output.metadata = {}
380
+ output.metadata.destructive = {
381
+ severity: match.severity,
382
+ category: match.category,
383
+ categoryLabel,
384
+ command: pattern,
385
+ warning: `${severityEmoji} ${match.severity.toUpperCase()}: ${categoryLabel}`,
386
+ }
387
+
388
+ console.warn(
389
+ `[destructive-check] ${severityEmoji} ${match.severity.toUpperCase()} destructive command detected`,
390
+ )
391
+ console.warn(` Category: ${categoryLabel}`)
392
+ console.warn(` Command: ${pattern.slice(0, 100)}${pattern.length > 100 ? "..." : ""}`)
393
+ console.warn(` ⚠️ This operation could cause data loss or system damage!`)
394
+
395
+ // Ask for permission for all severity levels
396
+ output.status = "ask"
397
+ return
398
+ }
399
+ }
400
+
401
+ // Also check joined patterns and metadata as fallback
402
+ const command = patterns.join(" ") || (input.metadata?.command as string) || input.title || ""
403
+ if (command && patterns.length === 0) {
404
+ const match = checkCommand(command)
405
+ if (match) {
406
+ stats.permissionsRequested++
407
+ global.permissionsRequested++
408
+ stats.lastMatch = match
409
+
410
+ // Add metadata to the permission request for UI display
411
+ const severityEmoji = match.severity === "critical" ? "🔴" : match.severity === "high" ? "🟠" : "🟡"
412
+ const categoryLabel = getCategoryLabel(match.category)
413
+
414
+ // Enhance metadata with destructive command information
415
+ if (!output.metadata) output.metadata = {}
416
+ output.metadata.destructive = {
417
+ severity: match.severity,
418
+ category: match.category,
419
+ categoryLabel,
420
+ command,
421
+ warning: `${severityEmoji} ${match.severity.toUpperCase()}: ${categoryLabel}`,
422
+ }
423
+
424
+ console.warn(
425
+ `[destructive-check] ${severityEmoji} ${match.severity.toUpperCase()} destructive command detected`,
426
+ )
427
+ console.warn(` Category: ${categoryLabel}`)
428
+ console.warn(` Command: ${command.slice(0, 100)}${command.length > 100 ? "..." : ""}`)
429
+ console.warn(` ⚠️ This operation could cause data loss or system damage!`)
430
+
431
+ // Ask for permission for all severity levels
432
+ output.status = "ask"
433
+ return
434
+ }
435
+ }
436
+ }
437
+
438
+ // Check file operations
439
+ if (permissionType === "write" || permissionType === "edit" || permissionType === "delete") {
440
+ const patterns =
441
+ input.patterns || (Array.isArray(input.pattern) ? input.pattern : input.pattern ? [input.pattern] : [])
442
+ for (const p of patterns) {
443
+ if (isDangerousPath(p)) {
444
+ stats.permissionsRequested++
445
+ global.permissionsRequested++
446
+
447
+ // Add metadata for dangerous file operations
448
+ if (!output.metadata) output.metadata = {}
449
+ output.metadata.destructive = {
450
+ severity: "critical",
451
+ category: "file",
452
+ categoryLabel: "Dangerous File Operation",
453
+ path: p,
454
+ warning: "🔴 CRITICAL: Dangerous file operation",
455
+ }
456
+
457
+ console.warn(`[destructive-check] 🔴 CRITICAL: Dangerous file operation detected`)
458
+ console.warn(` Operation: ${permissionType}`)
459
+ console.warn(` Path: ${p}`)
460
+ console.warn(` ⚠️ This operation could cause data loss or system damage!`)
461
+
462
+ // Ask for permission for dangerous file operations
463
+ output.status = "ask"
464
+ return
465
+ }
466
+ }
467
+ }
468
+ },
469
+
470
+ // After tool execution - log results for destructive operations
471
+ async ["tool.execute.after"](
472
+ hookInput: { tool: string; sessionID: string; callID: string },
473
+ result: { title: string; output: string; metadata: Record<string, unknown> },
474
+ ): Promise<void> {
475
+ const tool = hookInput.tool.toLowerCase()
476
+
477
+ // Log completion of potentially dangerous operations
478
+ if (tool === "bash" || tool === "shell") {
479
+ const output = result.output || ""
480
+ // Check for error messages that might indicate dangerous operation attempted
481
+ if (
482
+ output.includes("Permission denied") ||
483
+ output.includes("Operation not permitted") ||
484
+ output.includes("cannot remove") ||
485
+ output.includes("rm: refusing")
486
+ ) {
487
+ console.log(`[destructive-check] Dangerous operation was blocked by system`)
488
+ }
489
+ }
490
+ },
491
+ }
492
+ }
493
+
494
+ export default destructiveCheck