@aporthq/aport-agent-guardrails 1.0.10 → 1.0.12
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 +16 -3
- package/bin/aport-create-passport.sh +105 -1
- package/bin/aport-guardrail-bash.sh +157 -12
- package/bin/aport-status.sh +24 -0
- package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +31 -5
- package/docs/SECURITY_MODEL.md +608 -0
- package/docs/TOOL_POLICY_MAPPING.md +2 -0
- package/docs/VERIFICATION_METHODS.md +1 -0
- package/docs/frameworks/crewai.md +5 -0
- package/docs/frameworks/langchain.md +5 -0
- package/extensions/openclaw-aport/README.md +1 -1
- package/extensions/openclaw-aport/index.ts +105 -77
- package/extensions/openclaw-aport/node_modules/@types/node/LICENSE +21 -0
- package/extensions/openclaw-aport/node_modules/@types/node/README.md +15 -0
- package/extensions/openclaw-aport/node_modules/@types/node/assert/strict.d.ts +8 -0
- package/extensions/openclaw-aport/node_modules/@types/node/assert.d.ts +1005 -0
- package/extensions/openclaw-aport/node_modules/@types/node/async_hooks.d.ts +586 -0
- package/extensions/openclaw-aport/node_modules/@types/node/buffer.buffer.d.ts +457 -0
- package/extensions/openclaw-aport/node_modules/@types/node/buffer.d.ts +1901 -0
- package/extensions/openclaw-aport/node_modules/@types/node/child_process.d.ts +1453 -0
- package/extensions/openclaw-aport/node_modules/@types/node/cluster.d.ts +578 -0
- package/extensions/openclaw-aport/node_modules/@types/node/compatibility/disposable.d.ts +14 -0
- package/extensions/openclaw-aport/node_modules/@types/node/compatibility/index.d.ts +9 -0
- package/extensions/openclaw-aport/node_modules/@types/node/compatibility/indexable.d.ts +20 -0
- package/extensions/openclaw-aport/node_modules/@types/node/compatibility/iterators.d.ts +20 -0
- package/extensions/openclaw-aport/node_modules/@types/node/console.d.ts +452 -0
- package/extensions/openclaw-aport/node_modules/@types/node/constants.d.ts +21 -0
- package/extensions/openclaw-aport/node_modules/@types/node/crypto.d.ts +4504 -0
- package/extensions/openclaw-aport/node_modules/@types/node/dgram.d.ts +596 -0
- package/extensions/openclaw-aport/node_modules/@types/node/diagnostics_channel.d.ts +551 -0
- package/extensions/openclaw-aport/node_modules/@types/node/dns/promises.d.ts +477 -0
- package/extensions/openclaw-aport/node_modules/@types/node/dns.d.ts +860 -0
- package/extensions/openclaw-aport/node_modules/@types/node/domain.d.ts +170 -0
- package/extensions/openclaw-aport/node_modules/@types/node/events.d.ts +863 -0
- package/extensions/openclaw-aport/node_modules/@types/node/fs/promises.d.ts +1208 -0
- package/extensions/openclaw-aport/node_modules/@types/node/fs.d.ts +4332 -0
- package/extensions/openclaw-aport/node_modules/@types/node/globals.d.ts +170 -0
- package/extensions/openclaw-aport/node_modules/@types/node/globals.typedarray.d.ts +21 -0
- package/extensions/openclaw-aport/node_modules/@types/node/http.d.ts +1919 -0
- package/extensions/openclaw-aport/node_modules/@types/node/http2.d.ts +2580 -0
- package/extensions/openclaw-aport/node_modules/@types/node/https.d.ts +549 -0
- package/extensions/openclaw-aport/node_modules/@types/node/index.d.ts +92 -0
- package/extensions/openclaw-aport/node_modules/@types/node/inspector.generated.d.ts +2775 -0
- package/extensions/openclaw-aport/node_modules/@types/node/module.d.ts +503 -0
- package/extensions/openclaw-aport/node_modules/@types/node/net.d.ts +924 -0
- package/extensions/openclaw-aport/node_modules/@types/node/os.d.ts +480 -0
- package/extensions/openclaw-aport/node_modules/@types/node/package.json +145 -0
- package/extensions/openclaw-aport/node_modules/@types/node/path.d.ts +191 -0
- package/extensions/openclaw-aport/node_modules/@types/node/perf_hooks.d.ts +860 -0
- package/extensions/openclaw-aport/node_modules/@types/node/process.d.ts +1632 -0
- package/extensions/openclaw-aport/node_modules/@types/node/punycode.d.ts +117 -0
- package/extensions/openclaw-aport/node_modules/@types/node/querystring.d.ts +140 -0
- package/extensions/openclaw-aport/node_modules/@types/node/readline/promises.d.ts +154 -0
- package/extensions/openclaw-aport/node_modules/@types/node/readline.d.ts +715 -0
- package/extensions/openclaw-aport/node_modules/@types/node/repl.d.ts +430 -0
- package/extensions/openclaw-aport/node_modules/@types/node/stream/consumers.d.ts +38 -0
- package/extensions/openclaw-aport/node_modules/@types/node/stream/promises.d.ts +90 -0
- package/extensions/openclaw-aport/node_modules/@types/node/stream/web.d.ts +527 -0
- package/extensions/openclaw-aport/node_modules/@types/node/stream.d.ts +1680 -0
- package/extensions/openclaw-aport/node_modules/@types/node/string_decoder.d.ts +67 -0
- package/extensions/openclaw-aport/node_modules/@types/node/test.d.ts +1208 -0
- package/extensions/openclaw-aport/node_modules/@types/node/timers/promises.d.ts +108 -0
- package/extensions/openclaw-aport/node_modules/@types/node/timers.d.ts +286 -0
- package/extensions/openclaw-aport/node_modules/@types/node/tls.d.ts +1204 -0
- package/extensions/openclaw-aport/node_modules/@types/node/trace_events.d.ts +171 -0
- package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +455 -0
- package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +19 -0
- package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/index.d.ts +92 -0
- package/extensions/openclaw-aport/node_modules/@types/node/tty.d.ts +206 -0
- package/extensions/openclaw-aport/node_modules/@types/node/url.d.ts +957 -0
- package/extensions/openclaw-aport/node_modules/@types/node/util.d.ts +2083 -0
- package/extensions/openclaw-aport/node_modules/@types/node/v8.d.ts +753 -0
- package/extensions/openclaw-aport/node_modules/@types/node/vm.d.ts +704 -0
- package/extensions/openclaw-aport/node_modules/@types/node/wasi.d.ts +160 -0
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/abortcontroller.d.ts +34 -0
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/events.d.ts +81 -0
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/fetch.d.ts +38 -0
- package/extensions/openclaw-aport/node_modules/@types/node/worker_threads.d.ts +698 -0
- package/extensions/openclaw-aport/node_modules/@types/node/zlib.d.ts +517 -0
- package/extensions/openclaw-aport/node_modules/undici-types/README.md +6 -0
- package/extensions/openclaw-aport/node_modules/undici-types/agent.d.ts +31 -0
- package/extensions/openclaw-aport/node_modules/undici-types/api.d.ts +43 -0
- package/extensions/openclaw-aport/node_modules/undici-types/balanced-pool.d.ts +18 -0
- package/extensions/openclaw-aport/node_modules/undici-types/cache.d.ts +36 -0
- package/extensions/openclaw-aport/node_modules/undici-types/client.d.ts +97 -0
- package/extensions/openclaw-aport/node_modules/undici-types/connector.d.ts +34 -0
- package/extensions/openclaw-aport/node_modules/undici-types/content-type.d.ts +21 -0
- package/extensions/openclaw-aport/node_modules/undici-types/cookies.d.ts +28 -0
- package/extensions/openclaw-aport/node_modules/undici-types/diagnostics-channel.d.ts +67 -0
- package/extensions/openclaw-aport/node_modules/undici-types/dispatcher.d.ts +241 -0
- package/extensions/openclaw-aport/node_modules/undici-types/errors.d.ts +128 -0
- package/extensions/openclaw-aport/node_modules/undici-types/fetch.d.ts +209 -0
- package/extensions/openclaw-aport/node_modules/undici-types/file.d.ts +39 -0
- package/extensions/openclaw-aport/node_modules/undici-types/filereader.d.ts +54 -0
- package/extensions/openclaw-aport/node_modules/undici-types/formdata.d.ts +108 -0
- package/extensions/openclaw-aport/node_modules/undici-types/global-dispatcher.d.ts +9 -0
- package/extensions/openclaw-aport/node_modules/undici-types/global-origin.d.ts +7 -0
- package/extensions/openclaw-aport/node_modules/undici-types/handlers.d.ts +9 -0
- package/extensions/openclaw-aport/node_modules/undici-types/header.d.ts +4 -0
- package/extensions/openclaw-aport/node_modules/undici-types/index.d.ts +63 -0
- package/extensions/openclaw-aport/node_modules/undici-types/interceptors.d.ts +5 -0
- package/extensions/openclaw-aport/node_modules/undici-types/mock-agent.d.ts +50 -0
- package/extensions/openclaw-aport/node_modules/undici-types/mock-client.d.ts +25 -0
- package/extensions/openclaw-aport/node_modules/undici-types/mock-errors.d.ts +12 -0
- package/extensions/openclaw-aport/node_modules/undici-types/mock-interceptor.d.ts +93 -0
- package/extensions/openclaw-aport/node_modules/undici-types/mock-pool.d.ts +25 -0
- package/extensions/openclaw-aport/node_modules/undici-types/package.json +55 -0
- package/extensions/openclaw-aport/node_modules/undici-types/patch.d.ts +71 -0
- package/extensions/openclaw-aport/node_modules/undici-types/pool-stats.d.ts +19 -0
- package/extensions/openclaw-aport/node_modules/undici-types/pool.d.ts +28 -0
- package/extensions/openclaw-aport/node_modules/undici-types/proxy-agent.d.ts +30 -0
- package/extensions/openclaw-aport/node_modules/undici-types/readable.d.ts +61 -0
- package/extensions/openclaw-aport/node_modules/undici-types/webidl.d.ts +220 -0
- package/extensions/openclaw-aport/node_modules/undici-types/websocket.d.ts +131 -0
- package/extensions/openclaw-aport/package-lock.json +11225 -0
- package/external/aport-policies/README.md +10 -2
- package/external/aport-policies/code.release.publish.v1/policy.json +1 -1
- package/external/aport-policies/data.file.read.v1/policy.json +126 -0
- package/external/aport-policies/data.file.write.v1/policy.json +158 -0
- package/external/aport-policies/finance.payment.charge.v1/README.md +10 -1
- package/external/aport-policies/web.browser.v1/policy.json +161 -0
- package/external/aport-policies/web.fetch.v1/policy.json +149 -0
- package/external/aport-spec/integrations/shield/README.md +3 -3
- package/package.json +3 -2
- package/skills/aport-agent-guardrail/SKILL.md +260 -163
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ Your agent should **only do what you explicitly allow**. APort runs in the hook
|
|
|
60
60
|
|
|
61
61
|
| Policy | What it guards |
|
|
62
62
|
|--------|----------------|
|
|
63
|
-
| **system.command.execute.v1** | Shell commands — allowlist,
|
|
63
|
+
| **system.command.execute.v1** | Shell commands — allowlist, 50+ blocked patterns (`rm -rf`, `sudo`, `nc`, `find -exec rm`, injection); passport `allowed_paths` override for path rules |
|
|
64
64
|
| **mcp.tool.execute.v1** | MCP tool calls — server allowlist, rate limits |
|
|
65
65
|
| **messaging.message.send.v1** | Message sends — rate caps, capability checks |
|
|
66
66
|
| **agent.session.create.v1** / **agent.tool.register.v1** | Sessions and tool registration |
|
|
@@ -326,10 +326,23 @@ graph TB
|
|
|
326
326
|
|
|
327
327
|
</div>
|
|
328
328
|
|
|
329
|
-
- **Local-first:** Passport and policy live on your machine (or in repo); no cloud required for basic enforcement.
|
|
330
|
-
- **Fail-closed:** Missing or invalid passport → deny.
|
|
329
|
+
- **Local-first:** Passport and policy live on your machine (or in repo); no cloud required for basic enforcement.
|
|
330
|
+
- **Fail-closed:** Missing or invalid passport → deny.
|
|
331
331
|
- **Opt-in cloud:** Use API mode for global kill switch, signed receipts, and team sync.
|
|
332
332
|
|
|
333
|
+
### What APort Protects
|
|
334
|
+
|
|
335
|
+
**✅ Pre-action authorization (agent misbehavior):**
|
|
336
|
+
- **Prompt injection** - Hook-based enforcement; agent cannot bypass via prompts
|
|
337
|
+
- **Malicious skills** - Third-party OpenClaw skills validated before execution
|
|
338
|
+
- **Unauthorized commands** - Allowlist + 50+ blocked patterns (rm -rf, sudo, nc, find -exec rm, etc.)
|
|
339
|
+
- **Data exfiltration** - File access, messaging, web requests controlled by policy
|
|
340
|
+
- **Resource limits** - Rate limits, size caps, transaction amounts enforced
|
|
341
|
+
|
|
342
|
+
**Application-layer security model:** APort enforces policies at the agent action layer (between agent decision and tool execution). It operates within the OS trust boundary—standard for authorization systems like OAuth, IAM, and policy engines.
|
|
343
|
+
|
|
344
|
+
**For production:** Use API mode (`mode: api` with `agent_id`) for cryptographically signed decisions, protected passports, and global suspend. See [docs/SECURITY_MODEL.md](docs/SECURITY_MODEL.md) for full threat model, attack scenarios, and best practices.
|
|
345
|
+
|
|
333
346
|
---
|
|
334
347
|
|
|
335
348
|
## 🌐 When to use API vs local
|
|
@@ -119,11 +119,19 @@ if [ -n "$NON_INTERACTIVE" ]; then
|
|
|
119
119
|
exec_cap=y
|
|
120
120
|
msg_cap=y
|
|
121
121
|
data_cap=n
|
|
122
|
+
file_read_cap=n
|
|
123
|
+
file_write_cap=n
|
|
124
|
+
web_fetch_cap=n
|
|
125
|
+
web_browser_cap=n
|
|
122
126
|
max_pr_size=500
|
|
123
127
|
max_prs_per_day=10
|
|
124
128
|
max_msgs_per_day=100
|
|
125
129
|
allowed_repos_input="*"
|
|
126
130
|
exec_allow_scope="*"
|
|
131
|
+
file_read_paths="*"
|
|
132
|
+
file_write_paths="*"
|
|
133
|
+
web_fetch_domains="*"
|
|
134
|
+
web_browser_domains="*"
|
|
127
135
|
should_expire=n
|
|
128
136
|
never_expires="true"
|
|
129
137
|
expires_at=""
|
|
@@ -187,7 +195,7 @@ else
|
|
|
187
195
|
# Choose capabilities
|
|
188
196
|
echo " 🔐 Capabilities"
|
|
189
197
|
echo " ───────────────"
|
|
190
|
-
echo " Choose what your agent can do (y/n). Defaults: PRs, exec, and messaging = yes (matches README/docs);
|
|
198
|
+
echo " Choose what your agent can do (y/n). Defaults: PRs, exec, and messaging = yes (matches README/docs); others = no."
|
|
191
199
|
echo ""
|
|
192
200
|
read -p " • Create and merge pull requests? [Y/n]: " pr_cap
|
|
193
201
|
pr_cap=${pr_cap:-y}
|
|
@@ -198,6 +206,18 @@ else
|
|
|
198
206
|
read -p " • Send messages (email, SMS, etc.)? [Y/n]: " msg_cap
|
|
199
207
|
msg_cap=${msg_cap:-y}
|
|
200
208
|
|
|
209
|
+
read -p " • Read files from disk? [y/N]: " file_read_cap
|
|
210
|
+
file_read_cap=${file_read_cap:-n}
|
|
211
|
+
|
|
212
|
+
read -p " • Write/edit files on disk? [y/N]: " file_write_cap
|
|
213
|
+
file_write_cap=${file_write_cap:-n}
|
|
214
|
+
|
|
215
|
+
read -p " • Fetch data from web (HTTP requests)? [y/N]: " web_fetch_cap
|
|
216
|
+
web_fetch_cap=${web_fetch_cap:-n}
|
|
217
|
+
|
|
218
|
+
read -p " • Automate web browser? [y/N]: " web_browser_cap
|
|
219
|
+
web_browser_cap=${web_browser_cap:-n}
|
|
220
|
+
|
|
201
221
|
read -p " • Export data (database, files, etc.)? [y/N]: " data_cap
|
|
202
222
|
data_cap=${data_cap:-n}
|
|
203
223
|
|
|
@@ -230,6 +250,30 @@ else
|
|
|
230
250
|
max_msgs_per_day=${max_msgs_per_day:-100}
|
|
231
251
|
fi
|
|
232
252
|
|
|
253
|
+
if [ "$file_read_cap" = "y" ] || [ "$file_read_cap" = "Y" ]; then
|
|
254
|
+
echo " File read paths: default is allow any (*); sensitive patterns (.env, id_rsa, etc.) still blocked."
|
|
255
|
+
read -p " Allowed paths (comma-separated, * for all) [*]: " file_read_paths
|
|
256
|
+
file_read_paths=${file_read_paths:-"*"}
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
if [ "$file_write_cap" = "y" ] || [ "$file_write_cap" = "Y" ]; then
|
|
260
|
+
echo " File write paths: default is allow any (*); recommend restricting to project directories."
|
|
261
|
+
read -p " Allowed paths (comma-separated, * for all) [*]: " file_write_paths
|
|
262
|
+
file_write_paths=${file_write_paths:-"*"}
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
if [ "$web_fetch_cap" = "y" ] || [ "$web_fetch_cap" = "Y" ]; then
|
|
266
|
+
echo " Web fetch domains: default is allow any (*); private IPs (127.0.0.1, etc.) are blocked."
|
|
267
|
+
read -p " Allowed domains (comma-separated, * for all) [*]: " web_fetch_domains
|
|
268
|
+
web_fetch_domains=${web_fetch_domains:-"*"}
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
if [ "$web_browser_cap" = "y" ] || [ "$web_browser_cap" = "Y" ]; then
|
|
272
|
+
echo " Browser automation domains: default is allow any (*)."
|
|
273
|
+
read -p " Allowed domains (comma-separated, * for all) [*]: " web_browser_domains
|
|
274
|
+
web_browser_domains=${web_browser_domains:-"*"}
|
|
275
|
+
fi
|
|
276
|
+
|
|
233
277
|
if [ "$data_cap" = "y" ] || [ "$data_cap" = "Y" ]; then
|
|
234
278
|
read -p " Max export rows [10000]: " max_export_rows
|
|
235
279
|
max_export_rows=${max_export_rows:-10000}
|
|
@@ -292,6 +336,18 @@ fi
|
|
|
292
336
|
if [ "$msg_cap" = "y" ] || [ "$msg_cap" = "Y" ]; then
|
|
293
337
|
capabilities_json="$capabilities_json{\"id\": \"messaging.send\"},"
|
|
294
338
|
fi
|
|
339
|
+
if [ "$file_read_cap" = "y" ] || [ "$file_read_cap" = "Y" ]; then
|
|
340
|
+
capabilities_json="$capabilities_json{\"id\": \"data.file.read\"},"
|
|
341
|
+
fi
|
|
342
|
+
if [ "$file_write_cap" = "y" ] || [ "$file_write_cap" = "Y" ]; then
|
|
343
|
+
capabilities_json="$capabilities_json{\"id\": \"data.file.write\"},"
|
|
344
|
+
fi
|
|
345
|
+
if [ "$web_fetch_cap" = "y" ] || [ "$web_fetch_cap" = "Y" ]; then
|
|
346
|
+
capabilities_json="$capabilities_json{\"id\": \"web.fetch\"},"
|
|
347
|
+
fi
|
|
348
|
+
if [ "$web_browser_cap" = "y" ] || [ "$web_browser_cap" = "Y" ]; then
|
|
349
|
+
capabilities_json="$capabilities_json{\"id\": \"web.browser\"},"
|
|
350
|
+
fi
|
|
295
351
|
if [ "$data_cap" = "y" ] || [ "$data_cap" = "Y" ]; then
|
|
296
352
|
capabilities_json="$capabilities_json{\"id\": \"data.export\"},"
|
|
297
353
|
fi
|
|
@@ -330,6 +386,54 @@ if [ "$msg_cap" = "y" ] || [ "$msg_cap" = "Y" ]; then
|
|
|
330
386
|
limits_json="$limits_json\"msgs_per_min\": 5, \"msgs_per_day\": $max_msgs_per_day, \"allowed_recipients\": [\"*\"], \"approval_required\": false,"
|
|
331
387
|
fi
|
|
332
388
|
|
|
389
|
+
if [ "$file_read_cap" = "y" ] || [ "$file_read_cap" = "Y" ]; then
|
|
390
|
+
# Parse allowed paths
|
|
391
|
+
IFS=',' read -ra PATHS <<< "$file_read_paths"
|
|
392
|
+
allowed_paths_json="["
|
|
393
|
+
for path in "${PATHS[@]}"; do
|
|
394
|
+
path=$(echo "$path" | xargs) # trim whitespace
|
|
395
|
+
allowed_paths_json="$allowed_paths_json\"$path\","
|
|
396
|
+
done
|
|
397
|
+
allowed_paths_json="${allowed_paths_json%,}]"
|
|
398
|
+
limits_json="$limits_json\"data.file.read\": {\"allowed_paths\": $allowed_paths_json, \"max_file_size_mb\": 100},"
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
if [ "$file_write_cap" = "y" ] || [ "$file_write_cap" = "Y" ]; then
|
|
402
|
+
# Parse allowed paths
|
|
403
|
+
IFS=',' read -ra PATHS <<< "$file_write_paths"
|
|
404
|
+
allowed_paths_json="["
|
|
405
|
+
for path in "${PATHS[@]}"; do
|
|
406
|
+
path=$(echo "$path" | xargs) # trim whitespace
|
|
407
|
+
allowed_paths_json="$allowed_paths_json\"$path\","
|
|
408
|
+
done
|
|
409
|
+
allowed_paths_json="${allowed_paths_json%,}]"
|
|
410
|
+
limits_json="$limits_json\"data.file.write\": {\"allowed_paths\": $allowed_paths_json, \"max_file_size_mb\": 50},"
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
if [ "$web_fetch_cap" = "y" ] || [ "$web_fetch_cap" = "Y" ]; then
|
|
414
|
+
# Parse allowed domains
|
|
415
|
+
IFS=',' read -ra DOMAINS <<< "$web_fetch_domains"
|
|
416
|
+
allowed_domains_json="["
|
|
417
|
+
for domain in "${DOMAINS[@]}"; do
|
|
418
|
+
domain=$(echo "$domain" | xargs) # trim whitespace
|
|
419
|
+
allowed_domains_json="$allowed_domains_json\"$domain\","
|
|
420
|
+
done
|
|
421
|
+
allowed_domains_json="${allowed_domains_json%,}]"
|
|
422
|
+
limits_json="$limits_json\"web.fetch\": {\"allowed_domains\": $allowed_domains_json, \"blocked_domains\": [], \"max_requests_per_min\": 60},"
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
if [ "$web_browser_cap" = "y" ] || [ "$web_browser_cap" = "Y" ]; then
|
|
426
|
+
# Parse allowed domains
|
|
427
|
+
IFS=',' read -ra DOMAINS <<< "$web_browser_domains"
|
|
428
|
+
allowed_domains_json="["
|
|
429
|
+
for domain in "${DOMAINS[@]}"; do
|
|
430
|
+
domain=$(echo "$domain" | xargs) # trim whitespace
|
|
431
|
+
allowed_domains_json="$allowed_domains_json\"$domain\","
|
|
432
|
+
done
|
|
433
|
+
allowed_domains_json="${allowed_domains_json%,}]"
|
|
434
|
+
limits_json="$limits_json\"web.browser\": {\"allowed_domains\": $allowed_domains_json, \"max_screenshots_per_hour\": 100},"
|
|
435
|
+
fi
|
|
436
|
+
|
|
333
437
|
if [ "$data_cap" = "y" ] || [ "$data_cap" = "Y" ]; then
|
|
334
438
|
limits_json="$limits_json\"data.export\": {\"max_rows\": $max_export_rows, \"allow_pii\": $allow_pii_bool, \"allowed_collections\": [\"*\"]},"
|
|
335
439
|
fi
|
|
@@ -145,8 +145,9 @@ write_decision() {
|
|
|
145
145
|
issued_at: $issued_at,
|
|
146
146
|
expires_at: $expires_at,
|
|
147
147
|
passport_digest: $passport_digest,
|
|
148
|
-
signature: "
|
|
148
|
+
signature: "local-unsigned",
|
|
149
149
|
kid: "oap:local:dev-key",
|
|
150
|
+
verification_mode: "local",
|
|
150
151
|
prev_decision_id: (if $prev_decision_id == "" then null else $prev_decision_id end),
|
|
151
152
|
prev_content_hash: (if $prev_content_hash == "" then null else $prev_content_hash end)
|
|
152
153
|
}')
|
|
@@ -163,13 +164,17 @@ write_decision() {
|
|
|
163
164
|
# Update chain state for next decision (best-effort; do not block or fail the script)
|
|
164
165
|
echo "{\"last_decision_id\":\"$decision_id\",\"last_content_hash\":\"$content_hash\"}" > "$chain_state" 2> /dev/null || true
|
|
165
166
|
|
|
166
|
-
# Audit trail is non-core: append in background so it never blocks the tool call.
|
|
167
|
-
# Include capability context (command, recipient, repo/branch) when set.
|
|
168
167
|
audit_context=""
|
|
169
168
|
if [ -n "${CONTEXT_SUMMARY:-}" ]; then
|
|
170
169
|
audit_context=" context=\"${CONTEXT_SUMMARY}\""
|
|
171
170
|
fi
|
|
172
|
-
|
|
171
|
+
audit_entry="[$(date -u +%Y-%m-%d\ %H:%M:%S)] tool=$TOOL_NAME decision_id=$decision_id allow=$allow policy=$policy_id code=$deny_code${audit_context}"
|
|
172
|
+
|
|
173
|
+
if [ "$allow" = "false" ]; then
|
|
174
|
+
echo "$audit_entry" >> "$AUDIT_LOG" 2> /dev/null || true
|
|
175
|
+
else
|
|
176
|
+
(echo "$audit_entry" >> "$AUDIT_LOG") 2> /dev/null &
|
|
177
|
+
fi
|
|
173
178
|
|
|
174
179
|
if [ "$allow" = "true" ]; then
|
|
175
180
|
exit 0
|
|
@@ -211,20 +216,49 @@ fi
|
|
|
211
216
|
# Map tool to policy pack ID
|
|
212
217
|
POLICY_ID=""
|
|
213
218
|
case "$TOOL_NAME" in
|
|
214
|
-
git.create_pr | git.merge | git.push)
|
|
219
|
+
git.create_pr | git.merge | git.push | git.*)
|
|
215
220
|
POLICY_ID="code.repository.merge.v1"
|
|
216
221
|
;;
|
|
217
|
-
exec.run | exec.* | system.*)
|
|
222
|
+
exec | exec.run | exec.* | system.* | bash | shell | command)
|
|
218
223
|
POLICY_ID="system.command.execute.v1"
|
|
219
224
|
;;
|
|
220
|
-
|
|
225
|
+
gateway | gateway.* | process | process.*)
|
|
226
|
+
# High risk operations - treat as command execution
|
|
227
|
+
POLICY_ID="system.command.execute.v1"
|
|
228
|
+
;;
|
|
229
|
+
message.send | message.* | messaging.* | sms | whatsapp | slack | email)
|
|
221
230
|
POLICY_ID="messaging.message.send.v1"
|
|
222
231
|
;;
|
|
223
|
-
|
|
232
|
+
read | file.read | file.read.* | data.file.read | data.file.read.*)
|
|
233
|
+
POLICY_ID="data.file.read.v1"
|
|
234
|
+
;;
|
|
235
|
+
write | edit | file.write | file.write.* | file.edit | file.edit.* | data.file.write | data.file.write.*)
|
|
236
|
+
POLICY_ID="data.file.write.v1"
|
|
237
|
+
;;
|
|
238
|
+
web_fetch | webfetch | web.fetch | web.fetch.* | web_search | websearch | web.search | web.search.*)
|
|
239
|
+
POLICY_ID="web.fetch.v1"
|
|
240
|
+
;;
|
|
241
|
+
browser | browser.* | web.browser | web.browser.*)
|
|
242
|
+
POLICY_ID="web.browser.v1"
|
|
243
|
+
;;
|
|
244
|
+
mcp.*)
|
|
245
|
+
POLICY_ID="mcp.tool.execute.v1"
|
|
246
|
+
;;
|
|
247
|
+
agent.session.* | session.create | session.* | sessions.* | sessions_spawn | sessions_send)
|
|
248
|
+
POLICY_ID="agent.session.create.v1"
|
|
249
|
+
;;
|
|
250
|
+
cron | cron.*)
|
|
251
|
+
# Scheduled tasks - treat as session management
|
|
252
|
+
POLICY_ID="agent.session.create.v1"
|
|
253
|
+
;;
|
|
254
|
+
agent.tool.* | tool.register)
|
|
255
|
+
POLICY_ID="agent.tool.register.v1"
|
|
256
|
+
;;
|
|
257
|
+
payment.refund | refund | payment.charge | charge | payment.* | finance.*)
|
|
224
258
|
POLICY_ID="finance.payment.refund.v1"
|
|
225
259
|
;;
|
|
226
|
-
database.write | database.insert | database.update | database.delete | data.export)
|
|
227
|
-
POLICY_ID="data.export.v1"
|
|
260
|
+
database.write | database.insert | database.update | database.delete | data.export | data.export.*)
|
|
261
|
+
POLICY_ID="data.export.create.v1"
|
|
228
262
|
;;
|
|
229
263
|
*)
|
|
230
264
|
# Unknown tool - deny by default for security
|
|
@@ -232,7 +266,7 @@ case "$TOOL_NAME" in
|
|
|
232
266
|
;;
|
|
233
267
|
esac
|
|
234
268
|
|
|
235
|
-
# Capability-specific context summary for audit log (command, recipient, repo/branch, etc.)
|
|
269
|
+
# Capability-specific context summary for audit log (command, recipient, repo/branch, file_path, etc.)
|
|
236
270
|
CONTEXT_SUMMARY=""
|
|
237
271
|
if [ -n "$CONTEXT_JSON" ] && [ "$CONTEXT_JSON" != "{}" ]; then
|
|
238
272
|
if [[ "$POLICY_ID" == "system.command.execute"* ]]; then
|
|
@@ -244,6 +278,17 @@ if [ -n "$CONTEXT_JSON" ] && [ "$CONTEXT_JSON" != "{}" ]; then
|
|
|
244
278
|
BRANCH=$(echo "$CONTEXT_JSON" | jq -r '.branch // ""' 2> /dev/null || true)
|
|
245
279
|
[ -n "$REPO" ] && CONTEXT_SUMMARY="$REPO"
|
|
246
280
|
[ -n "$BRANCH" ] && CONTEXT_SUMMARY="${CONTEXT_SUMMARY:+$CONTEXT_SUMMARY }$BRANCH"
|
|
281
|
+
elif [[ "$POLICY_ID" == "data.file.read.v1" ]] || [[ "$POLICY_ID" == "data.file.write.v1" ]]; then
|
|
282
|
+
CONTEXT_SUMMARY=$(echo "$CONTEXT_JSON" | jq -r '.file_path // .path // ""' 2> /dev/null || true)
|
|
283
|
+
elif [[ "$POLICY_ID" == "web.fetch.v1" ]]; then
|
|
284
|
+
CONTEXT_SUMMARY=$(echo "$CONTEXT_JSON" | jq -r '.url // ""' 2> /dev/null || true)
|
|
285
|
+
elif [[ "$POLICY_ID" == "web.browser.v1" ]]; then
|
|
286
|
+
ACTION=$(echo "$CONTEXT_JSON" | jq -r '.action // ""' 2> /dev/null || true)
|
|
287
|
+
URL=$(echo "$CONTEXT_JSON" | jq -r '.url // ""' 2> /dev/null || true)
|
|
288
|
+
[ -n "$ACTION" ] && CONTEXT_SUMMARY="$ACTION"
|
|
289
|
+
[ -n "$URL" ] && CONTEXT_SUMMARY="${CONTEXT_SUMMARY:+$CONTEXT_SUMMARY }$URL"
|
|
290
|
+
elif [[ "$POLICY_ID" == "agent.session.create.v1" ]]; then
|
|
291
|
+
CONTEXT_SUMMARY=$(echo "$CONTEXT_JSON" | jq -r '.session_id // .agent_id // ""' 2> /dev/null || true)
|
|
247
292
|
fi
|
|
248
293
|
# Sanitize for one-line audit: no newlines, truncate, escape double quotes
|
|
249
294
|
if [ -n "$CONTEXT_SUMMARY" ]; then
|
|
@@ -360,7 +405,26 @@ if [[ "$POLICY_ID" == "system.command.execute"* ]]; then
|
|
|
360
405
|
write_decision false "$POLICY_ID" "oap.command_not_allowed" "Command '$COMMAND' is not in allowed list"
|
|
361
406
|
fi
|
|
362
407
|
|
|
363
|
-
# Check
|
|
408
|
+
# Check built-in security patterns (surgical - dangerous operations only)
|
|
409
|
+
# These are always enforced to prevent catastrophic damage
|
|
410
|
+
if [[ "$COMMAND" =~ rm[[:space:]]+-[^[:space:]]*r[^[:space:]]*f[^[:space:]]*[[:space:]]+/[[:space:]]*$ ]] \
|
|
411
|
+
|| [[ "$COMMAND" =~ rm[[:space:]]+-[^[:space:]]*r[^[:space:]]*f[^[:space:]]+/\* ]]; then
|
|
412
|
+
write_decision false "$POLICY_ID" "oap.dangerous_operation" "Destructive file operation: rm -rf / or rm -rf /*"
|
|
413
|
+
fi
|
|
414
|
+
if [[ "$COMMAND" =~ dd[[:space:]]+if=/dev/ ]]; then
|
|
415
|
+
write_decision false "$POLICY_ID" "oap.dangerous_operation" "Dangerous disk operation: dd if=/dev/"
|
|
416
|
+
fi
|
|
417
|
+
if [[ "$COMMAND" =~ mkfs\. ]]; then
|
|
418
|
+
write_decision false "$POLICY_ID" "oap.dangerous_operation" "Filesystem creation: mkfs"
|
|
419
|
+
fi
|
|
420
|
+
if [[ "$COMMAND" =~ (curl|wget)[[:space:]][^\|]*\|[[:space:]]*(bash|sh|zsh|python|node) ]]; then
|
|
421
|
+
write_decision false "$POLICY_ID" "oap.dangerous_operation" "Download-and-execute pattern detected"
|
|
422
|
+
fi
|
|
423
|
+
if [[ "$COMMAND" =~ :\(\)[[:space:]]*\{[[:space:]]*:[[:space:]]*\|[[:space:]]*:[[:space:]]*\&[[:space:]]*\} ]] || [[ "$COMMAND" =~ fork\(\) ]]; then
|
|
424
|
+
write_decision false "$POLICY_ID" "oap.dangerous_operation" "Fork bomb detected"
|
|
425
|
+
fi
|
|
426
|
+
|
|
427
|
+
# Check user-defined blocked patterns using safe pattern matching
|
|
364
428
|
while IFS= read -r pattern; do
|
|
365
429
|
[ -z "$pattern" ] && continue
|
|
366
430
|
# Use safe_pattern_match instead of bash glob patterns
|
|
@@ -390,5 +454,86 @@ if [[ "$POLICY_ID" == "messaging.message.send"* ]]; then
|
|
|
390
454
|
fi
|
|
391
455
|
fi
|
|
392
456
|
|
|
457
|
+
# File read policy evaluation
|
|
458
|
+
if [[ "$POLICY_ID" == "data.file.read.v1" ]]; then
|
|
459
|
+
FILE_PATH=$(echo "$CONTEXT_JSON" | jq -r '.file_path // .path // ""')
|
|
460
|
+
if [ -n "$FILE_PATH" ]; then
|
|
461
|
+
# Check allowed paths
|
|
462
|
+
PATH_ALLOWED=false
|
|
463
|
+
while IFS= read -r allowed_path; do
|
|
464
|
+
[ -z "$allowed_path" ] && continue
|
|
465
|
+
# Use safe prefix matching
|
|
466
|
+
if [[ "$FILE_PATH" == "$allowed_path"* ]] || [ "$allowed_path" = "*" ]; then
|
|
467
|
+
PATH_ALLOWED=true
|
|
468
|
+
break
|
|
469
|
+
fi
|
|
470
|
+
done < <(echo "$LIMITS" | jq -r '.allowed_paths[]? // empty')
|
|
471
|
+
|
|
472
|
+
HAS_ALLOWED=$(echo "$LIMITS" | jq -r '.allowed_paths | length' 2> /dev/null || echo "0")
|
|
473
|
+
if [ "$PATH_ALLOWED" = false ] && [ "${HAS_ALLOWED:-0}" -gt 0 ] 2> /dev/null; then
|
|
474
|
+
write_decision false "$POLICY_ID" "oap.path_not_allowed" "File path '$FILE_PATH' is not in allowed list"
|
|
475
|
+
fi
|
|
476
|
+
|
|
477
|
+
# Check blocked patterns (SSH keys, credentials, .env files)
|
|
478
|
+
while IFS= read -r pattern; do
|
|
479
|
+
[ -z "$pattern" ] && continue
|
|
480
|
+
# Simple glob-style matching for blocked patterns
|
|
481
|
+
if [[ "$FILE_PATH" == *"$pattern"* ]] || [[ "$FILE_PATH" == $pattern ]]; then
|
|
482
|
+
write_decision false "$POLICY_ID" "oap.blocked_pattern" "File path matches blocked pattern: $pattern"
|
|
483
|
+
fi
|
|
484
|
+
done < <(echo "$LIMITS" | jq -r '.blocked_patterns[]? // empty')
|
|
485
|
+
fi
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
# File write policy evaluation
|
|
489
|
+
if [[ "$POLICY_ID" == "data.file.write.v1" ]]; then
|
|
490
|
+
FILE_PATH=$(echo "$CONTEXT_JSON" | jq -r '.file_path // .path // ""')
|
|
491
|
+
if [ -n "$FILE_PATH" ]; then
|
|
492
|
+
# Check allowed paths
|
|
493
|
+
PATH_ALLOWED=false
|
|
494
|
+
while IFS= read -r allowed_path; do
|
|
495
|
+
[ -z "$allowed_path" ] && continue
|
|
496
|
+
# Use safe prefix matching
|
|
497
|
+
if [[ "$FILE_PATH" == "$allowed_path"* ]] || [ "$allowed_path" = "*" ]; then
|
|
498
|
+
PATH_ALLOWED=true
|
|
499
|
+
break
|
|
500
|
+
fi
|
|
501
|
+
done < <(echo "$LIMITS" | jq -r '.allowed_paths[]? // empty')
|
|
502
|
+
|
|
503
|
+
HAS_ALLOWED=$(echo "$LIMITS" | jq -r '.allowed_paths | length' 2> /dev/null || echo "0")
|
|
504
|
+
if [ "$PATH_ALLOWED" = false ] && [ "${HAS_ALLOWED:-0}" -gt 0 ] 2> /dev/null; then
|
|
505
|
+
write_decision false "$POLICY_ID" "oap.path_not_allowed" "File path '$FILE_PATH' is not in allowed list"
|
|
506
|
+
fi
|
|
507
|
+
|
|
508
|
+
# Check blocked paths (system directories)
|
|
509
|
+
while IFS= read -r blocked_path; do
|
|
510
|
+
[ -z "$blocked_path" ] && continue
|
|
511
|
+
# Prefix match for blocked system dirs
|
|
512
|
+
if [[ "$FILE_PATH" == "$blocked_path"* ]]; then
|
|
513
|
+
write_decision false "$POLICY_ID" "oap.path_blocked" "Writing to system directory is not allowed: $blocked_path"
|
|
514
|
+
fi
|
|
515
|
+
done < <(echo "$LIMITS" | jq -r '.blocked_paths[]? // empty')
|
|
516
|
+
|
|
517
|
+
# Check allowed extensions (if configured)
|
|
518
|
+
if [ -n "$(echo "$LIMITS" | jq -r '.allowed_extensions | length' 2> /dev/null)" ]; then
|
|
519
|
+
FILE_EXT=$(echo "$FILE_PATH" | grep -o '\.[^.]*$' | tr '[:upper:]' '[:lower:]')
|
|
520
|
+
if [ -n "$FILE_EXT" ]; then
|
|
521
|
+
EXT_ALLOWED=false
|
|
522
|
+
while IFS= read -r allowed_ext; do
|
|
523
|
+
[ -z "$allowed_ext" ] && continue
|
|
524
|
+
if [ "$FILE_EXT" = "$allowed_ext" ]; then
|
|
525
|
+
EXT_ALLOWED=true
|
|
526
|
+
break
|
|
527
|
+
fi
|
|
528
|
+
done < <(echo "$LIMITS" | jq -r '.allowed_extensions[]? // empty')
|
|
529
|
+
|
|
530
|
+
if [ "$EXT_ALLOWED" = false ]; then
|
|
531
|
+
write_decision false "$POLICY_ID" "oap.extension_not_allowed" "File extension $FILE_EXT is not allowed"
|
|
532
|
+
fi
|
|
533
|
+
fi
|
|
534
|
+
fi
|
|
535
|
+
fi
|
|
536
|
+
fi
|
|
537
|
+
|
|
393
538
|
# All checks passed - allow
|
|
394
539
|
write_decision true "$POLICY_ID" "oap.allowed" "All policy checks passed"
|
package/bin/aport-status.sh
CHANGED
|
@@ -129,6 +129,18 @@ jq -r '.limits | keys[]? // empty' $PASSPORT_FILE | while read policy; do
|
|
|
129
129
|
msgs_per_day=$(jq -r ".limits.\"$policy\".msgs_per_day // \"unlimited\"" $PASSPORT_FILE)
|
|
130
130
|
echo " - Messages/day: $msgs_per_day"
|
|
131
131
|
;;
|
|
132
|
+
"data.file.read")
|
|
133
|
+
allowed_paths=$(jq ".limits.\"$policy\".allowed_paths | length" $PASSPORT_FILE 2> /dev/null || echo "0")
|
|
134
|
+
blocked_patterns=$(jq ".limits.\"$policy\".blocked_patterns | length" $PASSPORT_FILE 2> /dev/null || echo "0")
|
|
135
|
+
echo " - Allowed paths: $allowed_paths"
|
|
136
|
+
echo " - Blocked patterns: $blocked_patterns"
|
|
137
|
+
;;
|
|
138
|
+
"data.file.write")
|
|
139
|
+
allowed_paths=$(jq ".limits.\"$policy\".allowed_paths | length" $PASSPORT_FILE 2> /dev/null || echo "0")
|
|
140
|
+
blocked_paths=$(jq ".limits.\"$policy\".blocked_paths | length" $PASSPORT_FILE 2> /dev/null || echo "0")
|
|
141
|
+
echo " - Allowed paths: $allowed_paths"
|
|
142
|
+
echo " - Blocked paths: $blocked_paths"
|
|
143
|
+
;;
|
|
132
144
|
"data.export")
|
|
133
145
|
max_rows=$(jq -r ".limits.\"$policy\".max_rows // \"unlimited\"" $PASSPORT_FILE)
|
|
134
146
|
allow_pii=$(jq -r ".limits.\"$policy\".allow_pii // false" $PASSPORT_FILE)
|
|
@@ -140,6 +152,18 @@ done
|
|
|
140
152
|
|
|
141
153
|
echo
|
|
142
154
|
|
|
155
|
+
# Protection coverage
|
|
156
|
+
echo "🛡️ APort Protection Coverage"
|
|
157
|
+
echo " System commands: ✅ Protected (system.command.execute.v1)"
|
|
158
|
+
echo " File reads: ✅ Protected (data.file.read.v1)"
|
|
159
|
+
echo " File writes: ✅ Protected (data.file.write.v1)"
|
|
160
|
+
echo " Messaging: ✅ Protected (messaging.message.send.v1)"
|
|
161
|
+
echo " Git operations: ✅ Protected (code.repository.merge.v1)"
|
|
162
|
+
echo " MCP tools: ✅ Protected (mcp.tool.execute.v1)"
|
|
163
|
+
echo
|
|
164
|
+
|
|
165
|
+
echo
|
|
166
|
+
|
|
143
167
|
# Latest decision (OAP v1.0 format)
|
|
144
168
|
if [ -f "$DECISION_FILE" ]; then
|
|
145
169
|
echo "🔍 Latest Decision"
|
|
@@ -27,11 +27,35 @@ Per the **Open Agent Passport (OAP) spec**, the passport has a **limits** object
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
## read, write, and
|
|
30
|
+
## read, write, and file operations (NOW PROTECTED)
|
|
31
|
+
|
|
32
|
+
- **read** and **write** are **now mapped** to APort policies:
|
|
33
|
+
- **read** → `data.file.read.v1` (enforces path allowlists, blocked patterns for SSH keys/credentials/.env)
|
|
34
|
+
- **write** → `data.file.write.v1` (enforces path allowlists, blocks system directories, optional extension restrictions)
|
|
35
|
+
- **Middleware param spreading:** The LangChain and CrewAI Node middlewares automatically parse tool input JSON and spread parameters (e.g. `file_path`) into the top-level verification context. This is required because the API's policy schema validates `file_path` as a required field. Without this, `read`/`write` tool calls would fail with 400 Bad Request.
|
|
36
|
+
- Configure passport limits for file operations:
|
|
37
|
+
- `limits.data.file.read.allowed_paths` - Array of allowed path prefixes (e.g. `["/tmp/*", "/home/user/projects/*"]`)
|
|
38
|
+
- `limits.data.file.read.blocked_patterns` - Array of patterns to block (e.g. `["**/.ssh/**", "**/.env"]`)
|
|
39
|
+
- `limits.data.file.write.allowed_paths` - Array of allowed write paths
|
|
40
|
+
- `limits.data.file.write.blocked_paths` - Array of system directories to block (e.g. `["/etc/**", "/bin/**"]`)
|
|
41
|
+
- Other tools like **edit**, **apply_patch**, **browser**, **cron**, **gateway**, **sessions_***, **nodes**, **image**, **web_search**, **web_fetch** remain **unmapped** and **allowed** by default (when `allowUnmappedTools: true`).
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Passport-configurable path overrides
|
|
46
|
+
|
|
47
|
+
The `system.command.execute.v1` policy includes hardcoded security patterns that block access to sensitive system directories (`/etc/`, `/sys/`, `/proc/`, etc.), sensitive hidden files, credential files, and more. Passport owners can override **path-sensitivity heuristics** by setting `limits.allowed_paths` or `limits.allowed_directories` in the passport:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"limits": {
|
|
52
|
+
"allowed_paths": ["/root/", "/home/agent/work/"],
|
|
53
|
+
"allowed_commands": ["*"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
When `allowed_paths` is set and the command references one of those paths, overridable rules (like "Access to sensitive system directories" or "Access to secrets and credentials files") are skipped. **Catastrophic protections are never overridable** — fork bombs, `rm -rf /`, reverse shells, `nc`/`netcat`, and `find -exec rm` are always blocked regardless of passport config.
|
|
35
59
|
|
|
36
60
|
---
|
|
37
61
|
|
|
@@ -42,8 +66,10 @@ Per the **Open Agent Passport (OAP) spec**, the passport has a **limits** object
|
|
|
42
66
|
| exec | system.command.execute.v1 | limits.system.command.execute |
|
|
43
67
|
| message, messaging.* | messaging.message.send.v1 | limits.messaging |
|
|
44
68
|
| git.* | code.repository.merge.v1 | limits.code.repository.merge |
|
|
69
|
+
| read | data.file.read.v1 | limits.data.file.read |
|
|
70
|
+
| write | data.file.write.v1 | limits.data.file.write |
|
|
45
71
|
| mcp.* | mcp.tool.execute.v1 | (API / MCP limits) |
|
|
46
|
-
|
|
|
72
|
+
| edit, browser, etc. | *(none)* | *(unmapped, allowed by default)* |
|
|
47
73
|
|
|
48
74
|
---
|
|
49
75
|
|