averecion-clawguard 2.7.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/dist/plugin.js ADDED
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.name = exports.id = void 0;
27
+ exports.register = register;
28
+ const fs = __importStar(require("fs"));
29
+ const path = __importStar(require("path"));
30
+ const os = __importStar(require("os"));
31
+ exports.id = "clawguard";
32
+ exports.name = "Clawguard";
33
+ const CLAWGUARD_DIR = path.join(os.homedir(), ".clawguard");
34
+ const CONFIG_FILE = path.join(CLAWGUARD_DIR, "config.json");
35
+ const LOG_DIR = path.join(CLAWGUARD_DIR, "logs");
36
+ const PENDING_DIR = path.join(CLAWGUARD_DIR, "pending");
37
+ const POLICY_FILE = path.join(CLAWGUARD_DIR, "policy.json");
38
+ const DEFAULT_POLICY = [
39
+ { tool: "exec", decision: "review", reason: "Terminal commands can modify your system" },
40
+ { tool: "bash", decision: "review", reason: "Terminal commands can modify your system" },
41
+ { tool: "shell", decision: "review", reason: "Terminal commands can modify your system" },
42
+ { tool: "write", decision: "review", reason: "File writes can overwrite important data" },
43
+ { tool: "delete", decision: "blocked", reason: "File deletion is high-risk" },
44
+ { tool: "unlink", decision: "blocked", reason: "File deletion is high-risk" },
45
+ { tool: "rm", decision: "blocked", reason: "File deletion is high-risk" },
46
+ ];
47
+ const DANGEROUS_PATTERNS = [
48
+ /rm\s+(-rf?|--recursive|--force)\s+[\/~]/i,
49
+ /rm\s+-[rf]{2}\s+/i,
50
+ /mkfs\./i,
51
+ /dd\s+if=.*of=\/dev/i,
52
+ />\s*\/dev\/sd[a-z]/i,
53
+ /chmod\s+777\s+\//i,
54
+ /curl.*\|\s*(ba)?sh/i,
55
+ /wget.*\|\s*(ba)?sh/i,
56
+ /:(){.*}:/i,
57
+ ];
58
+ const INJECTION_PATTERNS = [
59
+ /ignore\s+(previous|all|prior)\s+(instructions?|prompts?)/i,
60
+ /disregard\s+(your|the|all)\s+(instructions?|rules?|guidelines?)/i,
61
+ /you\s+are\s+now\s+(a|in|acting)/i,
62
+ /new\s+instructions?:/i,
63
+ /jailbreak/i,
64
+ /bypass\s+(security|filter|restriction)/i,
65
+ ];
66
+ function ensureDir(dir) {
67
+ if (!fs.existsSync(dir))
68
+ fs.mkdirSync(dir, { recursive: true });
69
+ }
70
+ function loadConfig() {
71
+ ensureDir(CLAWGUARD_DIR);
72
+ try {
73
+ if (fs.existsSync(CONFIG_FILE)) {
74
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
75
+ }
76
+ }
77
+ catch { }
78
+ return { enabled: true, consented: true, showMode: false, showModeActionsRemaining: 10, protectionLevel: "balanced" };
79
+ }
80
+ function saveConfig(config) {
81
+ ensureDir(CLAWGUARD_DIR);
82
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
83
+ }
84
+ function loadPolicy() {
85
+ try {
86
+ if (fs.existsSync(POLICY_FILE)) {
87
+ return JSON.parse(fs.readFileSync(POLICY_FILE, "utf-8"));
88
+ }
89
+ }
90
+ catch { }
91
+ return DEFAULT_POLICY;
92
+ }
93
+ function classifyTool(tool) {
94
+ const policy = loadPolicy();
95
+ const normalized = tool.toLowerCase();
96
+ return policy.find(r => normalized.includes(r.tool.toLowerCase())) || null;
97
+ }
98
+ function checkDangerousCommand(params) {
99
+ const paramsStr = JSON.stringify(params);
100
+ for (const pattern of DANGEROUS_PATTERNS) {
101
+ if (pattern.test(paramsStr)) {
102
+ return "Dangerous command pattern detected";
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ function checkInjection(params) {
108
+ const paramsStr = JSON.stringify(params);
109
+ for (const pattern of INJECTION_PATTERNS) {
110
+ if (pattern.test(paramsStr)) {
111
+ return "Possible prompt injection detected";
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+ function logAction(tool, params, allowed, reason) {
117
+ ensureDir(LOG_DIR);
118
+ const logEntry = {
119
+ ts: new Date().toISOString(),
120
+ type: "tool",
121
+ tool,
122
+ params,
123
+ allowed,
124
+ reason
125
+ };
126
+ const logFile = path.join(LOG_DIR, `${new Date().toISOString().split("T")[0]}.jsonl`);
127
+ fs.appendFileSync(logFile, JSON.stringify(logEntry) + "\n");
128
+ }
129
+ function generateApprovalId() {
130
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
131
+ }
132
+ function createPendingApproval(tool, params, reason) {
133
+ ensureDir(PENDING_DIR);
134
+ const id = generateApprovalId();
135
+ const approval = {
136
+ id,
137
+ tool,
138
+ params,
139
+ reason,
140
+ createdAt: new Date().toISOString(),
141
+ status: "pending"
142
+ };
143
+ fs.writeFileSync(path.join(PENDING_DIR, `${id}.json`), JSON.stringify(approval, null, 2));
144
+ return id;
145
+ }
146
+ async function waitForApproval(id, timeoutMs = 30000) {
147
+ const filePath = path.join(PENDING_DIR, `${id}.json`);
148
+ const startTime = Date.now();
149
+ const pollInterval = 500;
150
+ while (Date.now() - startTime < timeoutMs) {
151
+ try {
152
+ if (fs.existsSync(filePath)) {
153
+ const approval = JSON.parse(fs.readFileSync(filePath, "utf-8"));
154
+ if (approval.status !== "pending") {
155
+ return approval.status === "approved";
156
+ }
157
+ }
158
+ }
159
+ catch { }
160
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
161
+ }
162
+ return false;
163
+ }
164
+ function register(api) {
165
+ console.log("🛡️ Clawguard plugin registered (monitoring mode)");
166
+ console.log(" Note: before_tool_call not yet available in OpenClaw - using post-execution monitoring");
167
+ // Register for tool_result_persist (post-execution monitoring)
168
+ // This is synchronous and runs AFTER the tool executes
169
+ // We can log and analyze but cannot block execution
170
+ api.registerHook("tool_result_persist", (event) => {
171
+ const e = event;
172
+ const tool = (e.tool || e.toolName || "unknown");
173
+ const params = (e.args || e.params || {});
174
+ const result = e.result;
175
+ const config = loadConfig();
176
+ console.log(`🛡️ Clawguard: tool executed - ${tool}`);
177
+ if (!config.enabled || !config.consented) {
178
+ logAction(tool, params, true, "Clawguard not enabled");
179
+ return undefined; // Don't modify result
180
+ }
181
+ // Check for dangerous patterns (post-execution alert)
182
+ const injection = checkInjection(params);
183
+ if (injection) {
184
+ logAction(tool, params, false, `POST-EXEC ALERT: ${injection}`);
185
+ console.log(`🛡️ [ALERT] Detected after execution: ${injection}`);
186
+ }
187
+ const dangerous = checkDangerousCommand(params);
188
+ if (dangerous) {
189
+ logAction(tool, params, false, `POST-EXEC ALERT: ${dangerous}`);
190
+ console.log(`🛡️ [ALERT] Dangerous command detected: ${dangerous}`);
191
+ }
192
+ // Log the tool activity
193
+ const rule = classifyTool(tool);
194
+ if (rule) {
195
+ logAction(tool, params, true, `${rule.decision}: ${rule.reason}`);
196
+ }
197
+ else {
198
+ logAction(tool, params, true, "Executed (no policy match)");
199
+ }
200
+ // Update show mode counter
201
+ if (config.showMode) {
202
+ config.showModeActionsRemaining--;
203
+ if (config.showModeActionsRemaining <= 0)
204
+ config.showMode = false;
205
+ saveConfig(config);
206
+ }
207
+ return undefined; // Don't modify the result
208
+ }, { name: "clawguard-tool-monitor" });
209
+ // Also register for command events which ARE working
210
+ api.registerHook("command", (event) => {
211
+ const e = event;
212
+ const action = e.action;
213
+ const sessionKey = e.sessionKey;
214
+ console.log(`🛡️ Clawguard: command - ${action}`);
215
+ logAction(`command:${action}`, { sessionKey }, true, "Command event");
216
+ }, { name: "clawguard-command" });
217
+ }
218
+ exports.default = { id: exports.id, name: exports.name, register };
219
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA8LA,4BAsEC;AApQD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AAEZ,QAAA,EAAE,GAAG,WAAW,CAAC;AACjB,QAAA,IAAI,GAAG,WAAW,CAAC;AAEhC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;AACjD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AACxD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAyC5D,MAAM,cAAc,GAAiB;IACnC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,0CAA0C,EAAE;IACxF,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,0CAA0C,EAAE;IACxF,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,0CAA0C,EAAE;IACzF,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,0CAA0C,EAAE;IACzF,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,4BAA4B,EAAE;IAC7E,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,4BAA4B,EAAE;IAC7E,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,4BAA4B,EAAE;CAC1E,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,0CAA0C;IAC1C,mBAAmB;IACnB,SAAS;IACT,qBAAqB;IACrB,qBAAqB;IACrB,mBAAmB;IACnB,qBAAqB;IACrB,qBAAqB;IACrB,WAAW;CACZ,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,2DAA2D;IAC3D,kEAAkE;IAClE,kCAAkC;IAClC,uBAAuB;IACvB,YAAY;IACZ,yCAAyC;CAC1C,CAAC;AAEF,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,UAAU;IACjB,SAAS,CAAC,aAAa,CAAC,CAAC;IACzB,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC;AACxH,CAAC;AAED,SAAS,UAAU,CAAC,MAAuB;IACzC,SAAS,CAAC,aAAa,CAAC,CAAC;IACzB,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACtC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;AAC7E,CAAC;AAED,SAAS,qBAAqB,CAAC,MAA+B;IAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO,oCAAoC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,MAA+B;IACrD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO,oCAAoC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,MAA+B,EAAE,OAAgB,EAAE,MAAc;IAChG,SAAS,CAAC,OAAO,CAAC,CAAC;IACnB,MAAM,QAAQ,GAAG;QACf,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,IAAI,EAAE,MAAM;QACZ,IAAI;QACJ,MAAM;QACN,OAAO;QACP,MAAM;KACP,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACtF,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY,EAAE,MAA+B,EAAE,MAAc;IAC1F,SAAS,CAAC,WAAW,CAAC,CAAC;IACvB,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG;QACf,EAAE;QACF,IAAI;QACJ,MAAM;QACN,MAAM;QACN,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM,EAAE,SAAS;KAClB,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1F,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,EAAU,EAAE,SAAS,GAAG,KAAK;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,YAAY,GAAG,GAAG,CAAC;IAEzB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;gBAChE,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;oBAClC,OAAO,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC;gBACxC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QACV,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,QAAQ,CAAC,GAAc;IACrC,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,2FAA2F,CAAC,CAAC;IAEzG,+DAA+D;IAC/D,uDAAuD;IACvD,oDAAoD;IACpD,GAAG,CAAC,YAAY,CACd,qBAAqB,EACrB,CAAC,KAAc,EAAW,EAAE;QAC1B,MAAM,CAAC,GAAG,KAAgC,CAAC;QAC3C,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,SAAS,CAAW,CAAC;QAC3D,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,CAA4B,CAAC;QACrE,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QAExB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAE5B,OAAO,CAAC,GAAG,CAAC,kCAAkC,IAAI,EAAE,CAAC,CAAC;QAEtD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACzC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,CAAC,CAAC;YACvD,OAAO,SAAS,CAAC,CAAC,sBAAsB;QAC1C,CAAC;QAED,sDAAsD;QACtD,MAAM,SAAS,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,SAAS,EAAE,CAAC;YACd,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,SAAS,EAAE,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,yCAAyC,SAAS,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAChD,IAAI,SAAS,EAAE,CAAC;YACd,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,SAAS,EAAE,CAAC,CAAC;YAChE,OAAO,CAAC,GAAG,CAAC,2CAA2C,SAAS,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,wBAAwB;QACxB,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACT,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,CAAC,CAAC;QAC9D,CAAC;QAED,2BAA2B;QAC3B,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,CAAC,wBAAwB,EAAE,CAAC;YAClC,IAAI,MAAM,CAAC,wBAAwB,IAAI,CAAC;gBAAE,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;YAClE,UAAU,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;QAED,OAAO,SAAS,CAAC,CAAC,0BAA0B;IAC9C,CAAC,EACD,EAAE,IAAI,EAAE,wBAAwB,EAAE,CACnC,CAAC;IAEF,qDAAqD;IACrD,GAAG,CAAC,YAAY,CACd,SAAS,EACT,CAAC,KAAc,EAAQ,EAAE;QACvB,MAAM,CAAC,GAAG,KAAgC,CAAC;QAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,MAAgB,CAAC;QAClC,MAAM,UAAU,GAAG,CAAC,CAAC,UAAoB,CAAC;QAE1C,OAAO,CAAC,GAAG,CAAC,4BAA4B,MAAM,EAAE,CAAC,CAAC;QAClD,SAAS,CAAC,WAAW,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;IACxE,CAAC,EACD,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAC9B,CAAC;AACJ,CAAC;AAED,kBAAe,EAAE,EAAE,EAAF,UAAE,EAAE,IAAI,EAAJ,YAAI,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=postinstall.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postinstall.d.ts","sourceRoot":"","sources":["../src/postinstall.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const os = __importStar(require("os"));
30
+ const child_process_1 = require("child_process");
31
+ const CLAWGUARD_DIR = path.join(os.homedir(), ".clawguard");
32
+ function ensureDir(dir) {
33
+ if (!fs.existsSync(dir)) {
34
+ fs.mkdirSync(dir, { recursive: true });
35
+ }
36
+ }
37
+ function main() {
38
+ console.log("\n🛡️ Clawguard - AI Safety for OpenClaw\n");
39
+ ensureDir(CLAWGUARD_DIR);
40
+ ensureDir(path.join(CLAWGUARD_DIR, "logs"));
41
+ ensureDir(path.join(CLAWGUARD_DIR, "pending"));
42
+ const configPath = path.join(CLAWGUARD_DIR, "config.json");
43
+ if (!fs.existsSync(configPath)) {
44
+ const defaultConfig = {
45
+ enabled: false,
46
+ consented: false,
47
+ showMode: false,
48
+ showModeActionsRemaining: 10,
49
+ protectionLevel: "balanced",
50
+ installedAt: new Date().toISOString()
51
+ };
52
+ fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
53
+ }
54
+ try {
55
+ (0, child_process_1.execSync)("which clawdbot", { stdio: "ignore" });
56
+ console.log("To complete setup, run:");
57
+ console.log(" clawdbot hooks install <path-to-this-package>");
58
+ console.log(" clawdbot hooks enable clawguard");
59
+ console.log("");
60
+ }
61
+ catch {
62
+ console.log("Clawguard installed. When using with ClawdBot:");
63
+ console.log(" clawdbot hooks install @clawguard/openclaw-skill");
64
+ console.log(" clawdbot hooks enable clawguard");
65
+ console.log("");
66
+ }
67
+ console.log("Or start the dashboard directly:");
68
+ console.log(" npx averecion-lite start");
69
+ console.log("");
70
+ }
71
+ main();
72
+ //# sourceMappingURL=postinstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postinstall.js","sourceRoot":"","sources":["../src/postinstall.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AACzB,iDAAyC;AAEzC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AAE5D,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,SAAS,IAAI;IACX,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAE1D,SAAS,CAAC,aAAa,CAAC,CAAC;IACzB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5C,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;IAE/C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,aAAa,GAAG;YACpB,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,KAAK;YAChB,QAAQ,EAAE,KAAK;YACf,wBAAwB,EAAE,EAAE;YAC5B,eAAe,EAAE,UAAU;YAC3B,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACtC,CAAC;QACF,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,CAAC;QACH,IAAA,wBAAQ,EAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: before-tool-call
3
+ description: "Clawguard AI safety guardrails - intercepts tool calls before execution"
4
+ emoji: "🛡️"
5
+ events:
6
+ - tool:before
7
+ ---
8
+
9
+ # Clawguard Before Tool Call Hook
10
+
11
+ This hook intercepts all tool calls before execution to enforce safety policies:
12
+
13
+ - Blocks dangerous shell commands (rm -rf, curl|bash, etc.)
14
+ - Detects and blocks prompt injection attempts
15
+ - Requires approval for risky operations in balanced/strict mode
16
+ - Logs all actions for auditing
17
+
18
+ ## Protection Levels
19
+
20
+ - **Relaxed**: Minimal blocking, auto-approve reviews
21
+ - **Balanced**: Approval required for risky operations (default)
22
+ - **Strict**: Approval required for all potentially dangerous operations
@@ -0,0 +1,285 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as os from "os";
4
+
5
+ const CLAWGUARD_DIR = path.join(os.homedir(), ".clawguard");
6
+ const CONFIG_FILE = path.join(CLAWGUARD_DIR, "config.json");
7
+ const LOG_DIR = path.join(CLAWGUARD_DIR, "logs");
8
+ const PENDING_DIR = path.join(CLAWGUARD_DIR, "pending");
9
+ const POLICY_FILE = path.join(CLAWGUARD_DIR, "policy.json");
10
+
11
+ interface ClawguardConfig {
12
+ enabled: boolean;
13
+ consented: boolean;
14
+ showMode: boolean;
15
+ showModeActionsRemaining: number;
16
+ protectionLevel: "relaxed" | "balanced" | "strict";
17
+ }
18
+
19
+ interface PolicyRule {
20
+ tool: string;
21
+ decision: "approved" | "blocked" | "review";
22
+ reason: string;
23
+ }
24
+
25
+ const DEFAULT_POLICY: PolicyRule[] = [
26
+ { tool: "shell", decision: "review", reason: "Terminal commands can modify your system" },
27
+ { tool: "exec", decision: "review", reason: "Terminal commands can modify your system" },
28
+ { tool: "fs.write", decision: "review", reason: "File writes can overwrite important data" },
29
+ { tool: "fs.delete", decision: "blocked", reason: "File deletion is high-risk" },
30
+ { tool: "fs.unlink", decision: "blocked", reason: "File deletion is high-risk" },
31
+ { tool: "http", decision: "approved", reason: "Network requests allowed by default" },
32
+ { tool: "env.read", decision: "blocked", reason: "Credential access is restricted" },
33
+ { tool: "env.get", decision: "blocked", reason: "Credential access is restricted" },
34
+ { tool: "process", decision: "review", reason: "Process management requires review" }
35
+ ];
36
+
37
+ const DANGEROUS_PATTERNS = [
38
+ /rm\s+(-rf?|--recursive|--force)\s+[\/~]/i,
39
+ /rm\s+-[rf]{2}\s+/i,
40
+ /mkfs\./i,
41
+ /dd\s+if=.*of=\/dev/i,
42
+ />\s*\/dev\/sd[a-z]/i,
43
+ /chmod\s+777\s+\//i,
44
+ /curl.*\|\s*(ba)?sh/i,
45
+ /wget.*\|\s*(ba)?sh/i,
46
+ /eval\s*\(/i,
47
+ /:(){.*}:/i
48
+ ];
49
+
50
+ const INJECTION_PATTERNS = [
51
+ /ignore\s+(previous|all|prior)\s+(instructions?|prompts?)/i,
52
+ /disregard\s+(your|the|all)\s+(instructions?|rules?|guidelines?)/i,
53
+ /you\s+are\s+now\s+(a|in|acting)/i,
54
+ /new\s+instructions?:/i,
55
+ /system\s*:\s*you/i,
56
+ /\[INST\]/i,
57
+ /<\|im_start\|>/i,
58
+ /jailbreak/i,
59
+ /bypass\s+(security|filter|restriction)/i
60
+ ];
61
+
62
+ function ensureDir(dir: string): void {
63
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
64
+ }
65
+
66
+ function loadConfig(): ClawguardConfig {
67
+ ensureDir(CLAWGUARD_DIR);
68
+ try {
69
+ if (fs.existsSync(CONFIG_FILE)) {
70
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
71
+ }
72
+ } catch {}
73
+ return { enabled: false, consented: false, showMode: false, showModeActionsRemaining: 10, protectionLevel: "balanced" };
74
+ }
75
+
76
+ function saveConfig(config: ClawguardConfig): void {
77
+ ensureDir(CLAWGUARD_DIR);
78
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
79
+ }
80
+
81
+ function loadPolicy(): PolicyRule[] {
82
+ try {
83
+ if (fs.existsSync(POLICY_FILE)) {
84
+ return JSON.parse(fs.readFileSync(POLICY_FILE, "utf-8"));
85
+ }
86
+ } catch {}
87
+ return DEFAULT_POLICY;
88
+ }
89
+
90
+ function classifyTool(tool: string): PolicyRule | null {
91
+ const policy = loadPolicy();
92
+ return policy.find(r => tool.startsWith(r.tool) || tool === r.tool) || null;
93
+ }
94
+
95
+ function checkDangerousCommand(args: Record<string, unknown>): string | null {
96
+ const argsStr = JSON.stringify(args);
97
+ for (const pattern of DANGEROUS_PATTERNS) {
98
+ if (pattern.test(argsStr)) {
99
+ return `Dangerous command pattern detected`;
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+
105
+ function checkInjection(args: Record<string, unknown>): string | null {
106
+ const argsStr = JSON.stringify(args);
107
+ for (const pattern of INJECTION_PATTERNS) {
108
+ if (pattern.test(argsStr)) {
109
+ return `Prompt injection detected`;
110
+ }
111
+ }
112
+ return null;
113
+ }
114
+
115
+ function logAction(tool: string, args: Record<string, unknown>, allowed: boolean, reason: string): void {
116
+ ensureDir(LOG_DIR);
117
+ const logEntry = {
118
+ ts: new Date().toISOString(),
119
+ tool,
120
+ args,
121
+ allowed,
122
+ reason
123
+ };
124
+ const logFile = path.join(LOG_DIR, `${new Date().toISOString().split("T")[0]}.jsonl`);
125
+ fs.appendFileSync(logFile, JSON.stringify(logEntry) + "\n");
126
+ }
127
+
128
+ function generateApprovalId(): string {
129
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
130
+ }
131
+
132
+ function createPendingApproval(tool: string, args: Record<string, unknown>, reason: string): string {
133
+ ensureDir(PENDING_DIR);
134
+ const id = generateApprovalId();
135
+ const approval = {
136
+ id,
137
+ tool,
138
+ args,
139
+ reason,
140
+ createdAt: new Date().toISOString(),
141
+ status: "pending"
142
+ };
143
+ fs.writeFileSync(path.join(PENDING_DIR, `${id}.json`), JSON.stringify(approval, null, 2));
144
+ console.log(`\x1b[33m🛡️ Clawguard: Action requires approval: ${tool}\x1b[0m`);
145
+ console.log(` Reason: ${reason}`);
146
+ console.log(` Approve: clawdbot clawguard approve ${id}`);
147
+ return id;
148
+ }
149
+
150
+ async function waitForApproval(id: string, timeoutMs = 30000): Promise<boolean> {
151
+ const filePath = path.join(PENDING_DIR, `${id}.json`);
152
+ const startTime = Date.now();
153
+ const pollInterval = 500;
154
+
155
+ while (Date.now() - startTime < timeoutMs) {
156
+ try {
157
+ if (fs.existsSync(filePath)) {
158
+ const approval = JSON.parse(fs.readFileSync(filePath, "utf-8"));
159
+ if (approval.status !== "pending") {
160
+ return approval.status === "approved";
161
+ }
162
+ }
163
+ } catch {}
164
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
165
+ }
166
+
167
+ console.log(`\x1b[31m🛡️ Approval timed out - action blocked\x1b[0m`);
168
+ return false;
169
+ }
170
+
171
+ interface HookEvent {
172
+ type: string;
173
+ toolName: string;
174
+ args: Record<string, unknown>;
175
+ session?: unknown;
176
+ }
177
+
178
+ const handler = async (event: HookEvent): Promise<HookEvent | void> => {
179
+ if (event.type !== "before_tool_call") return;
180
+
181
+ const config = loadConfig();
182
+
183
+ if (!config.enabled || !config.consented) {
184
+ return;
185
+ }
186
+
187
+ const tool = event.toolName;
188
+ const args = event.args || {};
189
+
190
+ const injection = checkInjection(args);
191
+ if (injection) {
192
+ if (config.showMode) {
193
+ console.log(`\x1b[33m🛡️ [SHOW MODE] Would block: ${injection}\x1b[0m`);
194
+ config.showModeActionsRemaining--;
195
+ if (config.showModeActionsRemaining <= 0) config.showMode = false;
196
+ saveConfig(config);
197
+ logAction(tool, args, true, `showMode:${injection}`);
198
+ return;
199
+ }
200
+ logAction(tool, args, false, injection);
201
+ throw new Error(`🛡️ Blocked: ${injection}`);
202
+ }
203
+
204
+ const dangerous = checkDangerousCommand(args);
205
+ if (dangerous) {
206
+ if (config.showMode) {
207
+ console.log(`\x1b[33m🛡️ [SHOW MODE] Would block: ${dangerous}\x1b[0m`);
208
+ config.showModeActionsRemaining--;
209
+ if (config.showModeActionsRemaining <= 0) config.showMode = false;
210
+ saveConfig(config);
211
+ logAction(tool, args, true, `showMode:${dangerous}`);
212
+ return;
213
+ }
214
+ logAction(tool, args, false, dangerous);
215
+ throw new Error(`🛡️ Blocked: ${dangerous}`);
216
+ }
217
+
218
+ const rule = classifyTool(tool);
219
+
220
+ if (!rule) {
221
+ logAction(tool, args, true, "No policy match");
222
+ if (config.showMode) {
223
+ config.showModeActionsRemaining--;
224
+ if (config.showModeActionsRemaining <= 0) config.showMode = false;
225
+ saveConfig(config);
226
+ }
227
+ return;
228
+ }
229
+
230
+ if (rule.decision === "approved") {
231
+ logAction(tool, args, true, rule.reason);
232
+ if (config.showMode) {
233
+ config.showModeActionsRemaining--;
234
+ if (config.showModeActionsRemaining <= 0) config.showMode = false;
235
+ saveConfig(config);
236
+ }
237
+ return;
238
+ }
239
+
240
+ if (rule.decision === "blocked") {
241
+ if (config.showMode) {
242
+ console.log(`\x1b[33m🛡️ [SHOW MODE] Would block: ${tool} - ${rule.reason}\x1b[0m`);
243
+ config.showModeActionsRemaining--;
244
+ if (config.showModeActionsRemaining <= 0) config.showMode = false;
245
+ saveConfig(config);
246
+ logAction(tool, args, true, `showMode:blocked`);
247
+ return;
248
+ }
249
+ logAction(tool, args, false, rule.reason);
250
+ throw new Error(`🛡️ Blocked: ${rule.reason}`);
251
+ }
252
+
253
+ if (rule.decision === "review") {
254
+ if (config.protectionLevel === "relaxed") {
255
+ logAction(tool, args, true, "Relaxed mode - auto-approved");
256
+ if (config.showMode) {
257
+ config.showModeActionsRemaining--;
258
+ if (config.showModeActionsRemaining <= 0) config.showMode = false;
259
+ saveConfig(config);
260
+ }
261
+ return;
262
+ }
263
+
264
+ if (config.showMode) {
265
+ console.log(`\x1b[33m🛡️ [SHOW MODE] Would require approval: ${tool}\x1b[0m`);
266
+ config.showModeActionsRemaining--;
267
+ if (config.showModeActionsRemaining <= 0) config.showMode = false;
268
+ saveConfig(config);
269
+ logAction(tool, args, true, `showMode:review`);
270
+ return;
271
+ }
272
+
273
+ const approvalId = createPendingApproval(tool, args, rule.reason);
274
+ const approved = await waitForApproval(approvalId);
275
+ logAction(tool, args, approved, approved ? "manualApproval" : "denied/timeout");
276
+
277
+ if (!approved) {
278
+ throw new Error(`🛡️ Action denied or timed out`);
279
+ }
280
+ }
281
+
282
+ return;
283
+ };
284
+
285
+ export default handler;
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: clawguard
3
+ description: "AI safety guardrails - monitors actions, blocks dangerous commands, requires approval for risky operations"
4
+ homepage: https://github.com/averecion/clawguard
5
+ metadata:
6
+ {
7
+ "clawdbot":
8
+ {
9
+ "emoji": "🛡️",
10
+ "events": ["command"],
11
+ "install": [{ "id": "community", "kind": "community", "label": "Community Hook" }],
12
+ },
13
+ }
14
+ ---
15
+
16
+ # Clawguard
17
+
18
+ AI Safety for OpenClaw - monitors actions, blocks dangers, requires approval for risky operations.
19
+
20
+ ## What It Does
21
+
22
+ - **Prompt Injection Detection**: Blocks attempts to hijack your agent via malicious prompts
23
+ - **Dangerous Command Blocking**: Prevents rm -rf, fork bombs, curl|bash, and other destructive operations
24
+ - **Approval Workflow**: Risky actions require your explicit approval via CLI or dashboard
25
+ - **Activity Logging**: Full audit trail at `~/.clawguard/logs/`
26
+
27
+ ## Protection Levels
28
+
29
+ - **Relaxed**: Minimal blocking, auto-approves most actions
30
+ - **Balanced**: Asks before risky actions (recommended)
31
+ - **Strict**: Requires approval for everything
32
+
33
+ ## Dashboard
34
+
35
+ Visual monitoring at `http://localhost:4321/clawguard`
36
+
37
+ ## CLI Commands
38
+
39
+ ```bash
40
+ clawdbot clawguard status # Protection status
41
+ clawdbot clawguard pending # Pending approvals
42
+ clawdbot clawguard approve # Approve an action
43
+ clawdbot clawguard deny # Deny an action
44
+ clawdbot clawguard dashboard # Open visual dashboard
45
+ ```
46
+
47
+ ## First Run
48
+
49
+ On first use, Clawguard runs in **Show Mode** - observing 10 actions without blocking to demonstrate how it works. After Show Mode completes, full protection activates.
50
+
51
+ ## Configuration
52
+
53
+ Config stored at `~/.clawguard/config.json`