@clawdreyhepburn/carapace 0.3.0 → 0.3.2
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/cedar-engine-cedarling.ts +4 -0
- package/src/cedar-engine.ts +4 -0
- package/src/gui/html.ts +14 -3
- package/src/gui/server.ts +1 -0
- package/src/index.ts +149 -20
- package/src/types.ts +1 -0
|
@@ -1,442 +1,210 @@
|
|
|
1
1
|
# Recommended Policies
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cedar policy examples for common use cases. Copy, adapt, deploy.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**This is the most important step.** If you skip it, every policy in this document is advisory — the agent can just use OpenClaw's built-in `exec` tool instead of `carapace_exec` and bypass Cedar entirely.
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
openclaw carapace setup
|
|
11
|
-
openclaw gateway restart
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
This denies the built-in `exec`, `web_fetch`, and `web_search` tools, forcing agents to use the Cedar-gated `carapace_exec` and `carapace_fetch` instead. Verify with:
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
openclaw carapace check
|
|
18
|
-
# Should show: ✅ No bypass vulnerabilities found.
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**Without this, an agent can:**
|
|
22
|
-
- Call `exec` directly with `rm -rf /` — Carapace never sees it
|
|
23
|
-
- Call `web_fetch` to exfiltrate data — Carapace never sees it
|
|
24
|
-
- Call `exec` with `curl` to hit any API — Carapace never sees it
|
|
25
|
-
|
|
26
|
-
Run setup first. Then write policies.
|
|
27
|
-
|
|
28
|
-
## The Basics
|
|
29
|
-
|
|
30
|
-
Carapace defaults to **allow-all** so installing it never breaks anything. The recommended path:
|
|
31
|
-
|
|
32
|
-
1. **Run `carapace setup`** → close the bypass gap
|
|
33
|
-
2. **Add forbids** → block the scary stuff (this doc)
|
|
34
|
-
3. **Switch to deny-all** → explicitly permit only what's needed (advanced)
|
|
35
|
-
|
|
36
|
-
Most people should start with step 2 and stay there until they're comfortable.
|
|
5
|
+
> **First:** Complete the [Security Hardening Guide](SECURITY.md) — especially enabling the LLM proxy and closing the bypass gap. Without that, these policies are advisory.
|
|
37
6
|
|
|
38
7
|
---
|
|
39
8
|
|
|
40
9
|
## Shell Policies
|
|
41
10
|
|
|
42
|
-
### Block destructive
|
|
43
|
-
|
|
44
|
-
The classics. An agent with shell access can `rm -rf /` before you blink.
|
|
11
|
+
### Block destructive commands
|
|
45
12
|
|
|
46
13
|
```cedar
|
|
47
|
-
|
|
48
|
-
forbid(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
// Block destructive disk tools
|
|
55
|
-
forbid(
|
|
56
|
-
principal,
|
|
57
|
-
action == Jans::Action::"exec_command",
|
|
58
|
-
resource == Jans::Shell::"rmdir"
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
forbid(
|
|
62
|
-
principal,
|
|
63
|
-
action == Jans::Action::"exec_command",
|
|
64
|
-
resource == Jans::Shell::"mkfs"
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
forbid(
|
|
68
|
-
principal,
|
|
69
|
-
action == Jans::Action::"exec_command",
|
|
70
|
-
resource == Jans::Shell::"dd"
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
// Block format/partition tools
|
|
74
|
-
forbid(
|
|
75
|
-
principal,
|
|
76
|
-
action == Jans::Action::"exec_command",
|
|
77
|
-
resource == Jans::Shell::"diskutil"
|
|
78
|
-
);
|
|
14
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"rm");
|
|
15
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"rmdir");
|
|
16
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"mkfs");
|
|
17
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"dd");
|
|
18
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"diskutil");
|
|
19
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"format");
|
|
79
20
|
```
|
|
80
21
|
|
|
81
|
-
|
|
22
|
+
Use `trash` instead of `rm` — it's recoverable.
|
|
82
23
|
|
|
83
|
-
### Block
|
|
84
|
-
|
|
85
|
-
Agents don't need to read your SSH keys or browser passwords.
|
|
24
|
+
### Block privilege escalation
|
|
86
25
|
|
|
87
26
|
```cedar
|
|
88
|
-
|
|
89
|
-
forbid(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
forbid(
|
|
96
|
-
principal,
|
|
97
|
-
action == Jans::Action::"exec_command",
|
|
98
|
-
resource == Jans::Shell::"ssh-keygen"
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
forbid(
|
|
102
|
-
principal,
|
|
103
|
-
action == Jans::Action::"exec_command",
|
|
104
|
-
resource == Jans::Shell::"gpg"
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
// Block password managers
|
|
108
|
-
forbid(
|
|
109
|
-
principal,
|
|
110
|
-
action == Jans::Action::"exec_command",
|
|
111
|
-
resource == Jans::Shell::"op"
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
forbid(
|
|
115
|
-
principal,
|
|
116
|
-
action == Jans::Action::"exec_command",
|
|
117
|
-
resource == Jans::Shell::"pass"
|
|
118
|
-
);
|
|
27
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"sudo");
|
|
28
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"su");
|
|
29
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"chmod");
|
|
30
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"chown");
|
|
31
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"chgrp");
|
|
32
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"launchctl");
|
|
33
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"systemctl");
|
|
34
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"sc");
|
|
119
35
|
```
|
|
120
36
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
### Block system administration
|
|
124
|
-
|
|
125
|
-
Unless your agent is explicitly managing infrastructure, it shouldn't touch system config.
|
|
37
|
+
### Block credential access
|
|
126
38
|
|
|
127
39
|
```cedar
|
|
128
|
-
forbid(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
40
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"security");
|
|
41
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"ssh-keygen");
|
|
42
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"gpg");
|
|
43
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"op");
|
|
44
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"pass");
|
|
45
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"cmdkey");
|
|
46
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"certutil");
|
|
47
|
+
```
|
|
133
48
|
|
|
134
|
-
|
|
135
|
-
principal,
|
|
136
|
-
action == Jans::Action::"exec_command",
|
|
137
|
-
resource == Jans::Shell::"su"
|
|
138
|
-
);
|
|
49
|
+
### Block network recon
|
|
139
50
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
);
|
|
51
|
+
```cedar
|
|
52
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"nmap");
|
|
53
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"tcpdump");
|
|
54
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"netcat");
|
|
55
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"nc");
|
|
56
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"wireshark");
|
|
57
|
+
```
|
|
145
58
|
|
|
146
|
-
|
|
147
|
-
principal,
|
|
148
|
-
action == Jans::Action::"exec_command",
|
|
149
|
-
resource == Jans::Shell::"chown"
|
|
150
|
-
);
|
|
59
|
+
### Block shell wrappers
|
|
151
60
|
|
|
152
|
-
|
|
153
|
-
principal,
|
|
154
|
-
action == Jans::Action::"exec_command",
|
|
155
|
-
resource == Jans::Shell::"launchctl"
|
|
156
|
-
);
|
|
61
|
+
Prevent agents from using wrapper commands to run forbidden binaries:
|
|
157
62
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
);
|
|
63
|
+
```cedar
|
|
64
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"bash");
|
|
65
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"sh");
|
|
66
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"zsh");
|
|
67
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"env");
|
|
68
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"xargs");
|
|
69
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"cmd");
|
|
70
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"powershell");
|
|
163
71
|
```
|
|
164
72
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
### Block network reconnaissance
|
|
168
|
-
|
|
169
|
-
Agents don't need to scan your network.
|
|
73
|
+
### Allow safe dev tools
|
|
170
74
|
|
|
171
75
|
```cedar
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
);
|
|
76
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"git");
|
|
77
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"npm");
|
|
78
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"npx");
|
|
79
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"cat");
|
|
80
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"ls");
|
|
81
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"grep");
|
|
82
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"find");
|
|
83
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"mkdir");
|
|
84
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"trash");
|
|
85
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"cp");
|
|
86
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"mv");
|
|
87
|
+
```
|
|
183
88
|
|
|
184
|
-
|
|
185
|
-
principal,
|
|
186
|
-
action == Jans::Action::"exec_command",
|
|
187
|
-
resource == Jans::Shell::"netcat"
|
|
188
|
-
);
|
|
89
|
+
### Restrict sensitive path reads
|
|
189
90
|
|
|
91
|
+
```cedar
|
|
190
92
|
forbid(
|
|
191
93
|
principal,
|
|
192
94
|
action == Jans::Action::"exec_command",
|
|
193
|
-
resource == Jans::Shell::"
|
|
194
|
-
)
|
|
95
|
+
resource == Jans::Shell::"cat"
|
|
96
|
+
) when {
|
|
97
|
+
context.args like "*/.ssh/*" ||
|
|
98
|
+
context.args like "*/.aws/*" ||
|
|
99
|
+
context.args like "*/.env*" ||
|
|
100
|
+
context.args like "*/.gnupg/*"
|
|
101
|
+
};
|
|
195
102
|
```
|
|
196
103
|
|
|
197
|
-
###
|
|
198
|
-
|
|
199
|
-
If your agent does development work, permit the tools it actually needs:
|
|
104
|
+
### Block writes to OpenClaw directories
|
|
200
105
|
|
|
201
106
|
```cedar
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// Package managers
|
|
210
|
-
permit(
|
|
211
|
-
principal is Jans::Workload,
|
|
212
|
-
action == Jans::Action::"exec_command",
|
|
213
|
-
resource == Jans::Shell::"npm"
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
permit(
|
|
217
|
-
principal is Jans::Workload,
|
|
218
|
-
action == Jans::Action::"exec_command",
|
|
219
|
-
resource == Jans::Shell::"npx"
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
// Safe file operations
|
|
223
|
-
permit(
|
|
224
|
-
principal is Jans::Workload,
|
|
225
|
-
action == Jans::Action::"exec_command",
|
|
226
|
-
resource == Jans::Shell::"cat"
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
permit(
|
|
230
|
-
principal is Jans::Workload,
|
|
231
|
-
action == Jans::Action::"exec_command",
|
|
232
|
-
resource == Jans::Shell::"ls"
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
permit(
|
|
236
|
-
principal is Jans::Workload,
|
|
237
|
-
action == Jans::Action::"exec_command",
|
|
238
|
-
resource == Jans::Shell::"grep"
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
permit(
|
|
242
|
-
principal is Jans::Workload,
|
|
243
|
-
action == Jans::Action::"exec_command",
|
|
244
|
-
resource == Jans::Shell::"find"
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
permit(
|
|
248
|
-
principal is Jans::Workload,
|
|
249
|
-
action == Jans::Action::"exec_command",
|
|
250
|
-
resource == Jans::Shell::"trash"
|
|
251
|
-
);
|
|
107
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"cp")
|
|
108
|
+
when { context.args like "*/.openclaw/*" };
|
|
109
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"mv")
|
|
110
|
+
when { context.args like "*/.openclaw/*" };
|
|
111
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"tee")
|
|
112
|
+
when { context.args like "*/.openclaw/*" };
|
|
252
113
|
```
|
|
253
114
|
|
|
254
115
|
---
|
|
255
116
|
|
|
256
117
|
## API Policies
|
|
257
118
|
|
|
258
|
-
### Block data exfiltration
|
|
259
|
-
|
|
260
|
-
An agent that can POST to any URL can send your files, credentials, and chat history anywhere.
|
|
119
|
+
### Block data exfiltration services
|
|
261
120
|
|
|
262
121
|
```cedar
|
|
263
|
-
|
|
264
|
-
forbid(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
forbid(
|
|
271
|
-
principal,
|
|
272
|
-
action == Jans::Action::"call_api",
|
|
273
|
-
resource == Jans::API::"hastebin.com"
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
forbid(
|
|
277
|
-
principal,
|
|
278
|
-
action == Jans::Action::"call_api",
|
|
279
|
-
resource == Jans::API::"transfer.sh"
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
forbid(
|
|
283
|
-
principal,
|
|
284
|
-
action == Jans::Action::"call_api",
|
|
285
|
-
resource == Jans::API::"file.io"
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
forbid(
|
|
289
|
-
principal,
|
|
290
|
-
action == Jans::Action::"call_api",
|
|
291
|
-
resource == Jans::API::"webhook.site"
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
forbid(
|
|
295
|
-
principal,
|
|
296
|
-
action == Jans::Action::"call_api",
|
|
297
|
-
resource == Jans::API::"requestbin.com"
|
|
298
|
-
);
|
|
122
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"pastebin.com");
|
|
123
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"hastebin.com");
|
|
124
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"transfer.sh");
|
|
125
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"file.io");
|
|
126
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"webhook.site");
|
|
127
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"requestbin.com");
|
|
128
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"ngrok.io");
|
|
129
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"pipedream.net");
|
|
299
130
|
```
|
|
300
131
|
|
|
301
|
-
|
|
132
|
+
### Allow specific APIs
|
|
302
133
|
|
|
303
|
-
|
|
134
|
+
```cedar
|
|
135
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_api", resource == Jans::API::"api.github.com");
|
|
136
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_api", resource == Jans::API::"registry.npmjs.org");
|
|
137
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_api", resource == Jans::API::"api.yourcompany.com");
|
|
138
|
+
```
|
|
304
139
|
|
|
305
|
-
|
|
140
|
+
### Read-only API access
|
|
306
141
|
|
|
307
142
|
```cedar
|
|
308
|
-
// GitHub API
|
|
309
143
|
permit(
|
|
310
144
|
principal is Jans::Workload,
|
|
311
145
|
action == Jans::Action::"call_api",
|
|
312
146
|
resource == Jans::API::"api.github.com"
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
permit(
|
|
317
|
-
principal is Jans::Workload,
|
|
318
|
-
action == Jans::Action::"call_api",
|
|
319
|
-
resource == Jans::API::"registry.npmjs.org"
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
// Your own services
|
|
323
|
-
permit(
|
|
324
|
-
principal is Jans::Workload,
|
|
325
|
-
action == Jans::Action::"call_api",
|
|
326
|
-
resource == Jans::API::"api.yourcompany.com"
|
|
327
|
-
);
|
|
147
|
+
) when {
|
|
148
|
+
context.method == "GET"
|
|
149
|
+
};
|
|
328
150
|
```
|
|
329
151
|
|
|
330
152
|
### Block social media posting
|
|
331
153
|
|
|
332
|
-
If your agent has social media access, you might want to prevent it from posting without oversight, or block it from leaking info to random accounts.
|
|
333
|
-
|
|
334
154
|
```cedar
|
|
335
|
-
|
|
336
|
-
forbid(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
forbid(
|
|
343
|
-
principal,
|
|
344
|
-
action == Jans::Action::"call_api",
|
|
345
|
-
resource == Jans::API::"api.x.com"
|
|
346
|
-
);
|
|
155
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"api.twitter.com");
|
|
156
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"api.x.com");
|
|
157
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"graph.facebook.com");
|
|
158
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"api.linkedin.com");
|
|
159
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"discord.com");
|
|
160
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"slack.com");
|
|
161
|
+
```
|
|
347
162
|
|
|
348
|
-
|
|
349
|
-
principal,
|
|
350
|
-
action == Jans::Action::"call_api",
|
|
351
|
-
resource == Jans::API::"graph.facebook.com"
|
|
352
|
-
);
|
|
163
|
+
### Block localhost/internal network
|
|
353
164
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
);
|
|
165
|
+
```cedar
|
|
166
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"127.0.0.1");
|
|
167
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"0.0.0.0");
|
|
168
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"localhost");
|
|
359
169
|
```
|
|
360
170
|
|
|
361
|
-
**Why:** An agent that can post to social media can damage your reputation in seconds. Even if your agent "should" post, you probably want that gated through an MCP tool with its own policy rather than raw API access.
|
|
362
|
-
|
|
363
171
|
---
|
|
364
172
|
|
|
365
173
|
## MCP Tool Policies
|
|
366
174
|
|
|
367
|
-
### Block destructive
|
|
368
|
-
|
|
369
|
-
If you're using the filesystem MCP server, the write tools are the dangerous ones:
|
|
175
|
+
### Block destructive file tools
|
|
370
176
|
|
|
371
177
|
```cedar
|
|
372
|
-
forbid(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
resource == Jans::Tool::"filesystem/write_file"
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
forbid(
|
|
379
|
-
principal,
|
|
380
|
-
action == Jans::Action::"call_tool",
|
|
381
|
-
resource == Jans::Tool::"filesystem/move_file"
|
|
382
|
-
);
|
|
178
|
+
forbid(principal, action == Jans::Action::"call_tool", resource == Jans::Tool::"filesystem/write_file");
|
|
179
|
+
forbid(principal, action == Jans::Action::"call_tool", resource == Jans::Tool::"filesystem/move_file");
|
|
180
|
+
forbid(principal, action == Jans::Action::"call_tool", resource == Jans::Tool::"filesystem/delete_file");
|
|
383
181
|
```
|
|
384
182
|
|
|
385
183
|
### Block email mass operations
|
|
386
184
|
|
|
387
|
-
If your agent has email access via MCP:
|
|
388
|
-
|
|
389
185
|
```cedar
|
|
390
|
-
|
|
391
|
-
forbid(
|
|
392
|
-
|
|
393
|
-
action == Jans::Action::"call_tool",
|
|
394
|
-
resource == Jans::Tool::"email/delete_all"
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
forbid(
|
|
398
|
-
principal,
|
|
399
|
-
action == Jans::Action::"call_tool",
|
|
400
|
-
resource == Jans::Tool::"email/empty_trash"
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
// Prevent mass sending (spam)
|
|
404
|
-
forbid(
|
|
405
|
-
principal,
|
|
406
|
-
action == Jans::Action::"call_tool",
|
|
407
|
-
resource == Jans::Tool::"email/send_bulk"
|
|
408
|
-
);
|
|
186
|
+
forbid(principal, action == Jans::Action::"call_tool", resource == Jans::Tool::"email/delete_all");
|
|
187
|
+
forbid(principal, action == Jans::Action::"call_tool", resource == Jans::Tool::"email/empty_trash");
|
|
188
|
+
forbid(principal, action == Jans::Action::"call_tool", resource == Jans::Tool::"email/send_bulk");
|
|
409
189
|
```
|
|
410
190
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
### Block database mutations
|
|
414
|
-
|
|
415
|
-
If your agent has database access:
|
|
191
|
+
### Read-only database access
|
|
416
192
|
|
|
417
193
|
```cedar
|
|
418
|
-
forbid(
|
|
419
|
-
|
|
420
|
-
action == Jans::Action::"call_tool",
|
|
421
|
-
resource == Jans::Tool::"database/execute_sql"
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
// Allow reads only
|
|
425
|
-
permit(
|
|
426
|
-
principal is Jans::Workload,
|
|
427
|
-
action == Jans::Action::"call_tool",
|
|
428
|
-
resource == Jans::Tool::"database/query"
|
|
429
|
-
);
|
|
194
|
+
forbid(principal, action == Jans::Action::"call_tool", resource == Jans::Tool::"database/execute_sql");
|
|
195
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource == Jans::Tool::"database/query");
|
|
430
196
|
```
|
|
431
197
|
|
|
432
198
|
---
|
|
433
199
|
|
|
434
|
-
## Complete Starter
|
|
200
|
+
## Complete Starter Configurations
|
|
435
201
|
|
|
436
|
-
### "Cautious developer"
|
|
202
|
+
### "Cautious developer"
|
|
203
|
+
|
|
204
|
+
Good default for a coding agent. Allows dev tools, blocks destruction and exfiltration.
|
|
437
205
|
|
|
438
206
|
```cedar
|
|
439
|
-
//
|
|
207
|
+
// Dev tools
|
|
440
208
|
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"git");
|
|
441
209
|
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"npm");
|
|
442
210
|
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"npx");
|
|
@@ -450,70 +218,113 @@ permit(principal is Jans::Workload, action == Jans::Action::"exec_command", reso
|
|
|
450
218
|
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"cp");
|
|
451
219
|
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"mv");
|
|
452
220
|
|
|
221
|
+
// Hard blocks
|
|
453
222
|
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"rm");
|
|
454
223
|
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"sudo");
|
|
455
224
|
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"security");
|
|
225
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"bash");
|
|
226
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"sh");
|
|
456
227
|
|
|
457
|
-
//
|
|
228
|
+
// APIs
|
|
458
229
|
permit(principal is Jans::Workload, action == Jans::Action::"call_api", resource == Jans::API::"api.github.com");
|
|
459
230
|
permit(principal is Jans::Workload, action == Jans::Action::"call_api", resource == Jans::API::"registry.npmjs.org");
|
|
460
231
|
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"pastebin.com");
|
|
461
232
|
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"webhook.site");
|
|
462
233
|
|
|
463
|
-
// MCP: allow all tools
|
|
234
|
+
// MCP: allow all tools
|
|
464
235
|
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource);
|
|
465
236
|
```
|
|
466
237
|
|
|
467
|
-
### "
|
|
238
|
+
### "Research assistant"
|
|
468
239
|
|
|
469
|
-
|
|
240
|
+
Read-only access. Can browse and search but can't modify anything.
|
|
470
241
|
|
|
471
242
|
```cedar
|
|
472
|
-
//
|
|
243
|
+
// Read-only shell
|
|
244
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"cat");
|
|
245
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"ls");
|
|
246
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"grep");
|
|
247
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"find");
|
|
248
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"wc");
|
|
249
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"head");
|
|
250
|
+
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"tail");
|
|
251
|
+
|
|
252
|
+
// Read-only APIs (GET only)
|
|
253
|
+
permit(
|
|
254
|
+
principal is Jans::Workload,
|
|
255
|
+
action == Jans::Action::"call_api",
|
|
256
|
+
resource
|
|
257
|
+
) when {
|
|
258
|
+
context.method == "GET"
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Read-only MCP
|
|
473
262
|
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource == Jans::Tool::"filesystem/read_file");
|
|
474
263
|
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource == Jans::Tool::"filesystem/list_directory");
|
|
264
|
+
```
|
|
475
265
|
|
|
476
|
-
|
|
266
|
+
### "Paranoid lockdown"
|
|
267
|
+
|
|
268
|
+
Deny-all baseline. Nothing works unless explicitly permitted.
|
|
269
|
+
|
|
270
|
+
Set `defaultPolicy: "deny-all"` in config, then:
|
|
271
|
+
|
|
272
|
+
```cedar
|
|
273
|
+
// The absolute minimum for a useful agent
|
|
274
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource == Jans::Tool::"filesystem/read_file");
|
|
275
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource == Jans::Tool::"filesystem/list_directory");
|
|
477
276
|
permit(principal is Jans::Workload, action == Jans::Action::"exec_command", resource == Jans::Shell::"git");
|
|
478
277
|
|
|
479
|
-
// No API access
|
|
278
|
+
// No API access, no shell writes, no nothing else
|
|
480
279
|
```
|
|
481
280
|
|
|
482
|
-
|
|
281
|
+
### "Social media manager"
|
|
282
|
+
|
|
283
|
+
Can post to specific platforms via MCP tools, but not via raw API access.
|
|
284
|
+
|
|
285
|
+
```cedar
|
|
286
|
+
// Allow posting through the managed MCP tool (has its own guardrails)
|
|
287
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource == Jans::Tool::"twitter/post_tweet");
|
|
288
|
+
permit(principal is Jans::Workload, action == Jans::Action::"call_tool", resource == Jans::Tool::"twitter/read_timeline");
|
|
289
|
+
|
|
290
|
+
// Block raw API access to social platforms (bypass prevention)
|
|
291
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"api.twitter.com");
|
|
292
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"api.x.com");
|
|
293
|
+
|
|
294
|
+
// Block all shell access (social media agent doesn't need it)
|
|
295
|
+
forbid(principal, action == Jans::Action::"exec_command", resource);
|
|
296
|
+
|
|
297
|
+
// Block exfiltration
|
|
298
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"pastebin.com");
|
|
299
|
+
forbid(principal, action == Jans::Action::"call_api", resource == Jans::API::"webhook.site");
|
|
300
|
+
```
|
|
483
301
|
|
|
484
302
|
---
|
|
485
303
|
|
|
486
304
|
## Policy Design Principles
|
|
487
305
|
|
|
488
|
-
1. **Forbid the catastrophic
|
|
306
|
+
1. **Forbid the catastrophic first.** Block `rm`, `sudo`, and exfil domains before anything else.
|
|
489
307
|
|
|
490
|
-
2. **Forbid always wins.**
|
|
308
|
+
2. **Forbid always wins.** A `forbid` overrides any `permit` — write broad permits and surgical forbids.
|
|
491
309
|
|
|
492
|
-
3. **Binary name is the gate
|
|
310
|
+
3. **Binary name is the gate.** `Shell::"git"` permits *all* git commands. For subcommand control, use `when` conditions on `context.args`:
|
|
493
311
|
|
|
494
312
|
```cedar
|
|
495
|
-
forbid(
|
|
496
|
-
|
|
497
|
-
action == Jans::Action::"exec_command",
|
|
498
|
-
resource == Jans::Shell::"git"
|
|
499
|
-
) when {
|
|
500
|
-
context.args like "*--force*"
|
|
501
|
-
};
|
|
313
|
+
forbid(principal, action == Jans::Action::"exec_command", resource == Jans::Shell::"git")
|
|
314
|
+
when { context.args like "*push*--force*" };
|
|
502
315
|
```
|
|
503
316
|
|
|
504
|
-
4. **Domain name is the gate
|
|
317
|
+
4. **Domain name is the gate.** `API::"api.github.com"` permits all endpoints. For path/method control, use `when` conditions.
|
|
505
318
|
|
|
506
|
-
5. **
|
|
319
|
+
5. **Start with allow-all + forbids.** Switch to deny-all only when you understand your agent's full tool surface.
|
|
507
320
|
|
|
508
|
-
6. **Review regularly.**
|
|
321
|
+
6. **Review regularly.** Open the GUI, look at what's enabled, adjust.
|
|
509
322
|
|
|
510
323
|
---
|
|
511
324
|
|
|
512
325
|
## Further Reading
|
|
513
326
|
|
|
514
|
-
- [
|
|
515
|
-
- [Cedar
|
|
516
|
-
- [Cedar for AI Agents: When Forbid Meets Permit](https://clawdrey.com/blog/cedar-for-ai-agents-part-3-when-forbid-meets-permit.html)
|
|
517
|
-
- [Cedar for AI Agents: Proving It — SMT Solvers and Why I Trust Math More Than Tests](https://clawdrey.com/blog/proving-it-smt-solvers-and-why-i-trust-math-more-than-tests.html)
|
|
327
|
+
- [Security Hardening Guide](SECURITY.md) — OS-level protections, proxy setup, credential protection
|
|
328
|
+
- [Cedar blog series](https://clawdrey.com/blog/cedar-for-ai-agents-part-1-why-your-ai-agent-needs-a-policy-language.html)
|
|
518
329
|
- [Cedar Language Reference](https://docs.cedarpolicy.com/)
|
|
519
330
|
- [Carapace README](../README.md)
|