@aporthq/aport-agent-guardrails 1.0.26 → 1.0.27
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/bin/aport-claude-code-hook.sh +30 -14
- package/bin/aport-cursor-hook.sh +40 -15
- package/bin/aport-guardrail-bash.sh +15 -0
- package/bin/lib/hook-read-policy.sh +61 -0
- package/bin/lib/tool-mapping.sh +5 -1
- package/docs/FRAMEWORK_TOOL_MAPPING_AUDIT.md +158 -0
- package/docs/RELEASE.md +1 -1
- package/docs/TOOL_POLICY_MAPPING.md +6 -1
- package/docs/frameworks/claude-code.md +9 -5
- package/docs/frameworks/deerflow.md +12 -8
- package/extensions/openclaw-aport/CHANGELOG.md +2 -0
- package/extensions/openclaw-aport/local-evaluator.js +17 -0
- package/extensions/openclaw-aport/openclaw.plugin.json +1 -1
- package/extensions/openclaw-aport/package-lock.json +2 -2
- package/extensions/openclaw-aport/package.json +1 -1
- package/extensions/openclaw-aport/tool-mapping.js +33 -4
- package/package.json +1 -1
- package/python/aport_guardrails/core/tool-pack-mapping.json +111 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# APort Claude Code hook: reads tool_name + tool_input from JSON stdin
|
|
2
|
+
# APort Claude Code hook: reads tool_name + tool_input from JSON stdin (path-based Read uses guardrail).
|
|
3
3
|
# maps to APort policy, calls guardrail, outputs hookSpecificOutput deny or exit 0.
|
|
4
4
|
# Exit 0 = allow, exit 2 = block. Other exits = hook error (Claude Code may fail-open).
|
|
5
5
|
# Output format: Claude Code official schema (hookSpecificOutput.permissionDecision), NOT Cursor format.
|
|
@@ -14,6 +14,8 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
14
14
|
. "$ROOT_DIR/bin/aport-resolve-paths.sh"
|
|
15
15
|
# shellcheck source=bin/lib/guardrail-mode.sh
|
|
16
16
|
. "$ROOT_DIR/bin/lib/guardrail-mode.sh"
|
|
17
|
+
# shellcheck source=bin/lib/hook-read-policy.sh
|
|
18
|
+
. "$ROOT_DIR/bin/lib/hook-read-policy.sh"
|
|
17
19
|
load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.claude}"
|
|
18
20
|
|
|
19
21
|
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
@@ -49,7 +51,8 @@ set -e
|
|
|
49
51
|
if [ "$JQ_EXIT" -ne 0 ] || [ -z "$TOOL_NAME" ]; then
|
|
50
52
|
TOOL_NAME="unknown"
|
|
51
53
|
fi
|
|
52
|
-
|
|
54
|
+
# Strip permission-rule specifiers (e.g. Agent(Explore) -> Agent) before normalization.
|
|
55
|
+
TOOL_NAME_NORM="$(printf '%s' "$TOOL_NAME" | tr -d '[:space:]' | sed 's/^functions\.//' | sed 's/(.*$//' | tr '[:upper:]' '[:lower:]')"
|
|
53
56
|
set +e
|
|
54
57
|
TOOL_INPUT="$(echo "$INPUT" | jq -c '.tool_input // {}' 2> /dev/null)"
|
|
55
58
|
JQ_EXIT=$?
|
|
@@ -81,40 +84,45 @@ GUARDRAIL_TOOL=""
|
|
|
81
84
|
CONTEXT_JSON="{}"
|
|
82
85
|
|
|
83
86
|
case "$TOOL_NAME_NORM" in
|
|
84
|
-
bash | shell)
|
|
87
|
+
bash | shell | powershell | monitor)
|
|
85
88
|
GUARDRAIL_TOOL="bash"
|
|
86
|
-
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{command: (.command // "")}')"
|
|
89
|
+
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{command: (.command // .script // "")}')"
|
|
87
90
|
;;
|
|
88
|
-
read |
|
|
89
|
-
|
|
91
|
+
read | readfile | semanticsearch)
|
|
92
|
+
if ! aport_hook_try_read_evaluation "$TOOL_NAME_NORM" "$TOOL_INPUT"; then
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
95
|
+
;;
|
|
96
|
+
glob | ls | grep | lsp | todoread | toolsearch | askuserquestion | listmcpresourcestool | readmcpresourcetool | waitformcpservers)
|
|
97
|
+
# Search/list/read tools without a single file_path: allow without evaluator
|
|
90
98
|
exit 0
|
|
91
99
|
;;
|
|
92
|
-
taskget | tasklist | taskoutput | cronlist)
|
|
93
|
-
# Read-only task/cron queries: allow without evaluator
|
|
100
|
+
taskget | tasklist | taskoutput | cronlist | schedulewakeup | pushnotification)
|
|
101
|
+
# Read-only task/cron queries and notifications: allow without evaluator
|
|
94
102
|
exit 0
|
|
95
103
|
;;
|
|
96
104
|
enterplanmode | exitplanmode)
|
|
97
105
|
# Internal state transitions: allow without evaluator
|
|
98
106
|
exit 0
|
|
99
107
|
;;
|
|
100
|
-
write | edit | multiedit | notebookedit | todowrite | delete | strreplace | editnotebook)
|
|
108
|
+
write | edit | multiedit | notebookedit | todowrite | delete | strreplace | editnotebook | shareonboardingguide)
|
|
101
109
|
GUARDRAIL_TOOL="write"
|
|
102
110
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{file_path: (.file_path // .path // "")}')"
|
|
103
111
|
;;
|
|
104
112
|
websearch | webfetch)
|
|
105
|
-
GUARDRAIL_TOOL="
|
|
106
|
-
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // .query // "")}')"
|
|
113
|
+
GUARDRAIL_TOOL="websearch"
|
|
114
|
+
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // ""), query: (.query // "")}')"
|
|
107
115
|
;;
|
|
108
116
|
browser)
|
|
109
117
|
GUARDRAIL_TOOL="browser"
|
|
110
118
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // "")}')"
|
|
111
119
|
;;
|
|
112
|
-
task | taskcreate | taskupdate | taskstop |
|
|
120
|
+
agent | task | taskcreate | taskupdate | taskstop | skill | enterworktree | exitworktree | subagent | subagentstart | sendmessage | teamcreate | teamdelete | remotetrigger)
|
|
113
121
|
GUARDRAIL_TOOL="session.create"
|
|
114
|
-
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .prompt // "")}')"
|
|
122
|
+
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .prompt // .task // .message // ""), subagent_type: (.subagent_type // .agent_type // "")}')"
|
|
115
123
|
;;
|
|
116
124
|
croncreate | crondelete)
|
|
117
|
-
GUARDRAIL_TOOL="
|
|
125
|
+
GUARDRAIL_TOOL="session.create"
|
|
118
126
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .schedule // "")}')"
|
|
119
127
|
;;
|
|
120
128
|
mcp__* | mcp:* | callmcptool)
|
|
@@ -134,6 +142,14 @@ if [ -n "$HOOK_DECISION_FILE" ]; then
|
|
|
134
142
|
export OPENCLAW_DECISION_FILE="$HOOK_DECISION_FILE"
|
|
135
143
|
fi
|
|
136
144
|
|
|
145
|
+
# Read tools: send only file_path to the evaluator (Claude may attach large file bodies in tool_input).
|
|
146
|
+
if [ "$GUARDRAIL_TOOL" = "read" ]; then
|
|
147
|
+
CONTEXT_JSON="$(printf '%s' "$CONTEXT_JSON" | jq -c '{file_path: (.file_path // .path // "")}' 2> /dev/null || echo '{"file_path":""}')"
|
|
148
|
+
if [ -z "$(printf '%s' "$CONTEXT_JSON" | jq -r '.file_path // ""' 2> /dev/null)" ]; then
|
|
149
|
+
exit 0
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
|
|
137
153
|
# Call core evaluator (guardrail expects tool name, not policy ID)
|
|
138
154
|
set +e
|
|
139
155
|
"$GUARDRAIL" "$GUARDRAIL_TOOL" "$CONTEXT_JSON" 2> /dev/null
|
package/bin/aport-cursor-hook.sh
CHANGED
|
@@ -18,6 +18,8 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
18
18
|
. "$ROOT_DIR/bin/aport-resolve-paths.sh"
|
|
19
19
|
# shellcheck source=bin/lib/guardrail-mode.sh
|
|
20
20
|
. "$ROOT_DIR/bin/lib/guardrail-mode.sh"
|
|
21
|
+
# shellcheck source=bin/lib/hook-read-policy.sh
|
|
22
|
+
. "$ROOT_DIR/bin/lib/hook-read-policy.sh"
|
|
21
23
|
load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.cursor}"
|
|
22
24
|
|
|
23
25
|
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
@@ -82,8 +84,10 @@ HOOK_EVENT="$(echo "$INPUT" | jq -r '.hook_event_name // ""' 2> /dev/null)"
|
|
|
82
84
|
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name // ""' 2> /dev/null)"
|
|
83
85
|
|
|
84
86
|
if [ "$HOOK_EVENT" = "beforeReadFile" ] || { [ -z "$HOOK_EVENT" ] && [ -z "$TOOL_NAME" ] && echo "$INPUT" | jq -e '.file_path and .content' &> /dev/null; }; then
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
FILE_PATH="$(echo "$INPUT" | jq -r '.file_path // ""' 2> /dev/null || true)"
|
|
88
|
+
if ! aport_hook_try_read_evaluation_from_file_path "$FILE_PATH"; then
|
|
89
|
+
exit 0
|
|
90
|
+
fi
|
|
87
91
|
|
|
88
92
|
elif [ "$HOOK_EVENT" = "subagentStart" ] || { [ -z "$HOOK_EVENT" ] && echo "$INPUT" | jq -e '.subagent_id' &> /dev/null; }; then
|
|
89
93
|
# subagentStart: sub-agent spawning
|
|
@@ -96,36 +100,48 @@ elif [ "$HOOK_EVENT" = "beforeMCPExecution" ] || { [ -n "$TOOL_NAME" ] && echo "
|
|
|
96
100
|
CONTEXT_JSON="$(safe_jq "$INPUT" '{tool_name: (.tool_name // ""), tool_input: (.tool_input // {})}')"
|
|
97
101
|
|
|
98
102
|
elif [ -n "$TOOL_NAME" ]; then
|
|
99
|
-
# preToolUse:
|
|
100
|
-
#
|
|
101
|
-
TOOL_NORM="$(
|
|
103
|
+
# preToolUse: Shell, Read, Write, Grep, Delete, Task, WebSearch, Agent, MCP:*, etc.
|
|
104
|
+
# See docs/FRAMEWORK_TOOL_MAPPING_AUDIT.md and https://cursor.com/docs/agent/hooks
|
|
105
|
+
TOOL_NORM="$(printf '%s' "$TOOL_NAME" | tr -d '[:space:]' | sed 's/^functions\.//' | sed 's/(.*$//' | tr '[:upper:]' '[:lower:]')"
|
|
102
106
|
case "$TOOL_NORM" in
|
|
103
|
-
shell)
|
|
107
|
+
shell | bash)
|
|
104
108
|
GUARDRAIL_TOOL="bash"
|
|
105
109
|
CONTEXT_JSON="$(safe_jq "$INPUT" '{command: (.tool_input.command // "")}')"
|
|
106
110
|
;;
|
|
107
|
-
read |
|
|
108
|
-
|
|
111
|
+
read | readfile | semanticsearch)
|
|
112
|
+
TOOL_INPUT="$(safe_jq "$INPUT" '.tool_input // {}')"
|
|
113
|
+
if ! aport_hook_try_read_evaluation "$TOOL_NORM" "$TOOL_INPUT"; then
|
|
114
|
+
exit 0
|
|
115
|
+
fi
|
|
116
|
+
;;
|
|
117
|
+
grep | glob | ls | lsp | listmcpresourcestool | readmcpresourcetool | toolsearch | waitformcpservers | taskget | tasklist | taskoutput | cronlist)
|
|
109
118
|
exit 0
|
|
110
119
|
;;
|
|
111
|
-
write | strreplace | editnotebook)
|
|
120
|
+
write | strreplace | edit | multiedit | editnotebook | applypatch | notebookedit | delete)
|
|
112
121
|
GUARDRAIL_TOOL="write"
|
|
113
122
|
CONTEXT_JSON="$(safe_jq "$INPUT" '{file_path: (.tool_input.file_path // .tool_input.path // "")}')"
|
|
114
123
|
;;
|
|
115
|
-
|
|
116
|
-
GUARDRAIL_TOOL="
|
|
117
|
-
CONTEXT_JSON="$(safe_jq "$INPUT" '{
|
|
124
|
+
websearch | webfetch)
|
|
125
|
+
GUARDRAIL_TOOL="websearch"
|
|
126
|
+
CONTEXT_JSON="$(safe_jq "$INPUT" '{url: (.tool_input.url // ""), query: (.tool_input.query // "")}')"
|
|
127
|
+
;;
|
|
128
|
+
browser)
|
|
129
|
+
GUARDRAIL_TOOL="browser"
|
|
130
|
+
CONTEXT_JSON="$(safe_jq "$INPUT" '{url: (.tool_input.url // "")}')"
|
|
118
131
|
;;
|
|
119
|
-
task)
|
|
132
|
+
task | agent | taskcreate | taskupdate | taskstop | skill | subagent | subagentstart | sendmessage | teamcreate | teamdelete)
|
|
120
133
|
GUARDRAIL_TOOL="session.create"
|
|
121
134
|
CONTEXT_JSON="$(safe_jq "$INPUT" '{description: (.tool_input.description // .tool_input.prompt // "")}')"
|
|
122
135
|
;;
|
|
123
|
-
|
|
136
|
+
croncreate | crondelete)
|
|
137
|
+
GUARDRAIL_TOOL="session.create"
|
|
138
|
+
CONTEXT_JSON="$(safe_jq "$INPUT" '{description: (.tool_input.description // .tool_input.schedule // "")}')"
|
|
139
|
+
;;
|
|
140
|
+
mcp__* | mcp:* | callmcptool)
|
|
124
141
|
GUARDRAIL_TOOL="mcp.tool"
|
|
125
142
|
CONTEXT_JSON="$(safe_jq "$INPUT" '{tool_name: (.tool_name // ""), tool_input: (.tool_input // {})}')"
|
|
126
143
|
;;
|
|
127
144
|
*)
|
|
128
|
-
# Unknown preToolUse tool: fail-closed
|
|
129
145
|
deny "🛡️ APort: unknown tool '$TOOL_NAME' — fail-closed policy"
|
|
130
146
|
;;
|
|
131
147
|
esac
|
|
@@ -154,6 +170,15 @@ if [ -n "$HOOK_DECISION_FILE" ]; then
|
|
|
154
170
|
export OPENCLAW_DECISION_FILE="$HOOK_DECISION_FILE"
|
|
155
171
|
fi
|
|
156
172
|
|
|
173
|
+
# Read tools: send only file_path to the evaluator (Cursor may attach large file bodies in tool_input).
|
|
174
|
+
if [ "$GUARDRAIL_TOOL" = "read" ]; then
|
|
175
|
+
CONTEXT_JSON="$(printf '%s' "$CONTEXT_JSON" | jq -c '{file_path: (.file_path // .path // "")}' 2> /dev/null || echo '{"file_path":""}')"
|
|
176
|
+
if [ -z "$(printf '%s' "$CONTEXT_JSON" | jq -r '.file_path // ""' 2> /dev/null)" ]; then
|
|
177
|
+
echo '{"permission":"allow","allowed":true}'
|
|
178
|
+
exit 0
|
|
179
|
+
fi
|
|
180
|
+
fi
|
|
181
|
+
|
|
157
182
|
# Call core evaluator
|
|
158
183
|
set +e
|
|
159
184
|
"$GUARDRAIL" "$GUARDRAIL_TOOL" "$CONTEXT_JSON" 2> /dev/null
|
|
@@ -301,6 +301,17 @@ else
|
|
|
301
301
|
LIMITS=$(echo "$PASSPORT" | jq ".limits.\"$POLICY_BASE\" // {}")
|
|
302
302
|
fi
|
|
303
303
|
|
|
304
|
+
is_default_sensitive_read_path() {
|
|
305
|
+
local path_lower
|
|
306
|
+
path_lower="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
|
|
307
|
+
case "$path_lower" in
|
|
308
|
+
.env* | */.env* | .aws/* | */.aws/* | .ssh/* | */.ssh/* | *credentials* | *id_rsa* | *id_dsa* | *id_ecdsa* | *id_ed25519* | *.pem | *.key | *password* | .gnupg/* | */.gnupg/* | .kube/* | */.kube/*)
|
|
309
|
+
return 0
|
|
310
|
+
;;
|
|
311
|
+
esac
|
|
312
|
+
return 1
|
|
313
|
+
}
|
|
314
|
+
|
|
304
315
|
# Evaluate policy-specific limits
|
|
305
316
|
if [[ "$POLICY_ID" == "code.repository.merge"* ]]; then
|
|
306
317
|
FILES_CHANGED=$(echo "$CONTEXT_JSON" | jq -r '.files_changed // .files // 0')
|
|
@@ -426,6 +437,10 @@ fi
|
|
|
426
437
|
if [[ "$POLICY_ID" == "data.file.read.v1" ]]; then
|
|
427
438
|
FILE_PATH=$(echo "$CONTEXT_JSON" | jq -r '.file_path // .path // ""')
|
|
428
439
|
if [ -n "$FILE_PATH" ]; then
|
|
440
|
+
if is_default_sensitive_read_path "$FILE_PATH"; then
|
|
441
|
+
write_decision false "$POLICY_ID" "oap.blocked_pattern" "File path matches default sensitive read pattern"
|
|
442
|
+
fi
|
|
443
|
+
|
|
429
444
|
# Check allowed paths
|
|
430
445
|
PATH_ALLOWED=false
|
|
431
446
|
while IFS= read -r allowed_path; do
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Shared path-based read tool handling for Claude Code and Cursor hooks.
|
|
2
|
+
# shellcheck shell=bash
|
|
3
|
+
#
|
|
4
|
+
# Usage (after defining safe_jq in the hook):
|
|
5
|
+
# aport_hook_try_read_evaluation "$TOOL_NORM" "$TOOL_INPUT"
|
|
6
|
+
# On success: sets GUARDRAIL_TOOL=read and CONTEXT_JSON with file_path; returns 0.
|
|
7
|
+
# On skip (no path / not a path-based read tool): returns 1 — caller may allow without evaluator.
|
|
8
|
+
|
|
9
|
+
aport_hook_read_tools_with_path() {
|
|
10
|
+
case "$1" in
|
|
11
|
+
read | readfile | semanticsearch) return 0 ;;
|
|
12
|
+
*) return 1 ;;
|
|
13
|
+
esac
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
aport_hook_read_context_json() {
|
|
17
|
+
local file_path="$1"
|
|
18
|
+
jq -n -c --arg file_path "$file_path" '{file_path: $file_path}' 2> /dev/null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
aport_hook_try_read_evaluation() {
|
|
22
|
+
local tool_norm="$1"
|
|
23
|
+
local tool_input="$2"
|
|
24
|
+
local file_path context
|
|
25
|
+
|
|
26
|
+
if ! aport_hook_read_tools_with_path "$tool_norm"; then
|
|
27
|
+
return 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
file_path="$(echo "$tool_input" | jq -r '.file_path // .path // ""' 2> /dev/null || true)"
|
|
31
|
+
if [ -z "$file_path" ]; then
|
|
32
|
+
return 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
context="$(aport_hook_read_context_json "$file_path")"
|
|
36
|
+
if [ -z "$context" ]; then
|
|
37
|
+
return 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
GUARDRAIL_TOOL="read"
|
|
41
|
+
CONTEXT_JSON="$context"
|
|
42
|
+
return 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
aport_hook_try_read_evaluation_from_file_path() {
|
|
46
|
+
local file_path="$1"
|
|
47
|
+
local context
|
|
48
|
+
|
|
49
|
+
if [ -z "$file_path" ]; then
|
|
50
|
+
return 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
context="$(aport_hook_read_context_json "$file_path")"
|
|
54
|
+
if [ -z "$context" ]; then
|
|
55
|
+
return 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
GUARDRAIL_TOOL="read"
|
|
59
|
+
CONTEXT_JSON="$context"
|
|
60
|
+
return 0
|
|
61
|
+
}
|
package/bin/lib/tool-mapping.sh
CHANGED
|
@@ -5,6 +5,7 @@ _aport_tool_mapping_file() {
|
|
|
5
5
|
lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" && pwd)"
|
|
6
6
|
|
|
7
7
|
local candidates=(
|
|
8
|
+
"$lib_dir/../../packages/core/src/core/tool-pack-mapping.json"
|
|
8
9
|
"$lib_dir/../../python/aport_guardrails/core/tool-pack-mapping.json"
|
|
9
10
|
"$lib_dir/../python/aport_guardrails/core/tool-pack-mapping.json"
|
|
10
11
|
"$lib_dir/../../aport/runtime/python/aport_guardrails/core/tool-pack-mapping.json"
|
|
@@ -26,7 +27,7 @@ resolve_policy_id_from_tool_name() {
|
|
|
26
27
|
local normalized=""
|
|
27
28
|
local policy_id=""
|
|
28
29
|
|
|
29
|
-
normalized="$(printf '%s' "$tool_name" | tr '[:upper:]' '[:lower:]')"
|
|
30
|
+
normalized="$(printf '%s' "$tool_name" | tr '[:upper:]' '[:lower:]' | sed 's/^functions\.//' | sed 's/(.*$//')"
|
|
30
31
|
mapping_file="$(_aport_tool_mapping_file)" || return 1
|
|
31
32
|
|
|
32
33
|
policy_id="$(
|
|
@@ -43,6 +44,9 @@ resolve_policy_id_from_tool_name() {
|
|
|
43
44
|
' "$mapping_file"
|
|
44
45
|
)"
|
|
45
46
|
|
|
47
|
+
# Do not apply JSON "default" here: bash/API guardrail must deny unmapped tool names.
|
|
48
|
+
# Python/TypeScript adapters use tool_to_pack_id() / toolToPackId() which apply default for custom tools.
|
|
49
|
+
|
|
46
50
|
if [[ -n "$policy_id" && "$policy_id" != "null" ]]; then
|
|
47
51
|
printf '%s\n' "$policy_id"
|
|
48
52
|
return 0
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Framework tool mapping audit
|
|
2
|
+
|
|
3
|
+
Last reviewed against upstream docs and this repo’s mapping sources.
|
|
4
|
+
|
|
5
|
+
## Mapping layers (do not conflate)
|
|
6
|
+
|
|
7
|
+
| Layer | Source | Used by |
|
|
8
|
+
|-------|--------|---------|
|
|
9
|
+
| **Host tool name** | Framework-native string (`Bash`, `exec`, `sessions_spawn`, LangChain tool `.name`) | Hooks / plugin receive this |
|
|
10
|
+
| **Guardrail tool id** | Hook alias passed to `aport-guardrail-bash.sh` (`bash`, `session.create`, `cron`, …) | Claude Code / Cursor hooks only |
|
|
11
|
+
| **Policy pack id** | `tool-pack-mapping.json` or `mapToolToPolicy()` | All evaluators |
|
|
12
|
+
| **Passport capability** | `external/aport-policies/*/policy.json` → `requires_capabilities` | Passport + evaluator |
|
|
13
|
+
|
|
14
|
+
Canonical pack mapping for **generic** tool names: `packages/core/src/core/tool-pack-mapping.json` (mirrored in `python/aport_guardrails/core/tool-pack-mapping.json`). TypeScript/Python adapters call `toolToPackId()` / `tool_to_pack_id()`. Bash/API guardrails use `bin/lib/tool-mapping.sh` (same JSON + `default` fallback).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Framework summary
|
|
19
|
+
|
|
20
|
+
| Framework | Integration | Tool mapping implementation | Unmapped tools |
|
|
21
|
+
|-----------|-------------|----------------------------|----------------|
|
|
22
|
+
| **Claude Code** | `bin/aport-claude-code-hook.sh` | Host name → guardrail id → JSON | **Fail-closed** (deny) |
|
|
23
|
+
| **Cursor** | `bin/aport-cursor-hook.sh` | Same pattern as Claude Code | **Fail-closed** |
|
|
24
|
+
| **OpenClaw** | `extensions/openclaw-aport/tool-mapping.js` | `mapToolToPolicy()` in plugin | **Allow** if `allowUnmappedTools: true` (default) |
|
|
25
|
+
| **LangChain / LangGraph** | `APortCallback` / `APortGuardrailCallback` | `tool_to_pack_id(tool.name)` only | Uses JSON `default` → `system.command.execute.v1` |
|
|
26
|
+
| **CrewAI** | Python/TS hook middleware | `tool_to_pack_id(tool_name)` only | Same as LangChain |
|
|
27
|
+
| **DeerFlow** | `OAPGuardrailProvider` | `tool_to_pack_id(tool_name)` only | Same as LangChain |
|
|
28
|
+
| **n8n** | Not shipped | — | — |
|
|
29
|
+
| **Generic CLI** | `aport-guardrail-bash.sh` | JSON only | Deny if no rule and no default |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Claude Code
|
|
34
|
+
|
|
35
|
+
**Official tools** ([Tools reference](https://code.claude.com/docs/en/tools-reference)) include: `Agent`, `Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `WebSearch`, `WebFetch`, `Skill`, `TaskCreate`/`TaskUpdate`/`TaskStop`/`TaskGet`/`TaskList`, `CronCreate`/`CronDelete`/`CronList`, `EnterWorktree`/`ExitWorktree`, `PowerShell`, `Monitor`, MCP tools (`mcp__server__tool`), etc.
|
|
36
|
+
|
|
37
|
+
| Host tool | Guardrail id | Policy pack | Capability |
|
|
38
|
+
|-----------|--------------|-------------|------------|
|
|
39
|
+
| `Bash`, `PowerShell`, `Monitor` | `bash` | `system.command.execute.v1` | `system.command.execute` |
|
|
40
|
+
| `Read`, `Glob`, `Grep`, `LSP`, MCP list/read/wait tools, `TaskGet`/`TaskList`, `CronList`, … | *(allow, no evaluator)* | — | — |
|
|
41
|
+
| `Write`, `Edit`, `MultiEdit`, `NotebookEdit`, `TodoWrite`, … | `write` | `data.file.write.v1` | `data.file.write` |
|
|
42
|
+
| `WebSearch`, `WebFetch` | `websearch` | `web.fetch.v1` | `web.fetch` |
|
|
43
|
+
| `Browser` | `browser` | `web.browser.v1` | `web.browser` |
|
|
44
|
+
| `Agent`, `Task`, `TaskCreate`, `Skill`, worktree, teams, … | `session.create` | `agent.session.create.v1` | `agent.session.create` |
|
|
45
|
+
| `CronCreate`, `CronDelete` | `cron` | `agent.session.create.v1` | `agent.session.create` |
|
|
46
|
+
| `mcp__*`, `CallMcpTool` | `mcp.tool` | `mcp.tool.execute.v1` | `mcp.tool.execute` |
|
|
47
|
+
|
|
48
|
+
Programmatic map: `packages/claude-code/src/claudeCodeTools.ts`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Cursor
|
|
53
|
+
|
|
54
|
+
**Documented preToolUse tools** ([Cursor hooks](https://cursor.com/docs/agent/hooks)): `Shell`, `Read`, `Write`, `Grep`, `Delete`, `Task`, `WebSearch` (when emitted), `Agent`, `Edit`, MCP (`MCP:…` / `mcp:…`), plus `beforeShellExecution` / `subagentStart` events.
|
|
55
|
+
|
|
56
|
+
Hook aligned with Claude Code (v1.0.27+): `WebSearch`, `WebFetch`, `Agent`, `Edit`, `ApplyPatch`, `browser`, `cron`, MCP variants. Read-family tools still skip the evaluator for latency.
|
|
57
|
+
|
|
58
|
+
**Caveats (upstream, not APort):**
|
|
59
|
+
|
|
60
|
+
- Built-in web search may not always emit `preToolUse` depending on Cursor model/routing ([forum report](https://forum.cursor.com/t/bug-pretooluse-agent-hooks-do-not-emit-for-built-in-web-search-in-cursor-agent-works-in-claude-code/160761)).
|
|
61
|
+
- `ApplyPatch` may not emit hooks in some CLI builds ([Codex issue #16732](https://github.com/openai/codex/issues/16732)) — APort cannot enforce what the host does not emit.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## OpenClaw
|
|
66
|
+
|
|
67
|
+
**Official session tools** ([Session tools](https://docs.openclaw.ai/concepts/session-tool)): `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `sessions_yield`, `subagents`, `session_status`. Plus `exec`, `message`, `read`/`write`/`edit`, `git.*`, MCP `server__tool`, etc.
|
|
68
|
+
|
|
69
|
+
| OpenClaw tool | Policy pack | Capability |
|
|
70
|
+
|---------------|-------------|------------|
|
|
71
|
+
| `exec`, `exec.*`, `process`, `gateway` | `system.command.execute.v1` | `system.command.execute` |
|
|
72
|
+
| `message` (send-family actions) | `messaging.message.send.v1` | `messaging.message.send` |
|
|
73
|
+
| `read`, `view`, `glob` | `data.file.read.v1` | `data.file.read` |
|
|
74
|
+
| `write`, `edit`, `multiedit` | `data.file.write.v1` | `data.file.write` |
|
|
75
|
+
| `web_search`, `webfetch`, `browser` | `web.fetch.v1` / `web.browser.v1` | `web.fetch` / `web.browser` |
|
|
76
|
+
| `sessions_spawn`, `sessions_send`, `sessions_yield`, `subagents`, `session_status` | `agent.session.create.v1` | `agent.session.create` |
|
|
77
|
+
| `sessions_list`, `sessions_history` | `data.file.read.v1` | `data.file.read` |
|
|
78
|
+
| `server__tool`, `mcp.*` | `mcp.tool.execute.v1` | `mcp.tool.execute` |
|
|
79
|
+
| `git.*` | `code.repository.merge.v1` | `repo.merge` / `repo.pr.create` |
|
|
80
|
+
|
|
81
|
+
Plugin: `extensions/openclaw-aport/tool-mapping.js`. With `allowUnmappedTools: false`, unmapped tools are denied.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## LangChain / CrewAI / DeerFlow
|
|
86
|
+
|
|
87
|
+
No host-specific tool names: mapping is **only** `tool-pack-mapping.json` on whatever name the tool registers (e.g. `bash`, `read_file`, `web_search`, custom `my_tool`).
|
|
88
|
+
|
|
89
|
+
DeerFlow-tested names (see `python/aport_guardrails/tests/test_oap_provider_e2e.py`):
|
|
90
|
+
|
|
91
|
+
| Tool name | Policy pack |
|
|
92
|
+
|-----------|-------------|
|
|
93
|
+
| `bash` | `system.command.execute.v1` |
|
|
94
|
+
| `write_file`, `str_replace` | `data.file.write.v1` |
|
|
95
|
+
| `read_file`, `ls`, `present_file`, `view_image` | `data.file.read.v1` |
|
|
96
|
+
| `web_search` | `web.fetch.v1` |
|
|
97
|
+
| `git.create_pr` | `code.repository.merge.v1` |
|
|
98
|
+
| `mcp.github.issues` | `mcp.tool.execute.v1` |
|
|
99
|
+
| `messaging.send` | `messaging.message.send.v1` |
|
|
100
|
+
|
|
101
|
+
Custom LangChain tools must use names that match a prefix/substring in `tool-pack-mapping.json`, or they resolve to **`default`** (`system.command.execute.v1`).
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Policy packs reference
|
|
106
|
+
|
|
107
|
+
| Policy pack | Capability id | Notes |
|
|
108
|
+
|-------------|---------------|--------|
|
|
109
|
+
| `system.command.execute.v1` | `system.command.execute` | Shell/exec; JSON `default` |
|
|
110
|
+
| `data.file.read.v1` | `data.file.read` | |
|
|
111
|
+
| `data.file.write.v1` | `data.file.write` | |
|
|
112
|
+
| `web.fetch.v1` | `web.fetch` | WebSearch/WebFetch |
|
|
113
|
+
| `web.browser.v1` | `web.browser` | Browser automation (no separate `cron.v1`) |
|
|
114
|
+
| `agent.session.create.v1` | `agent.session.create` | Subagents, tasks, **cron** guardrail alias `cron` |
|
|
115
|
+
| `mcp.tool.execute.v1` | `mcp.tool.execute` | |
|
|
116
|
+
| `messaging.message.send.v1` | `messaging.message.send` | |
|
|
117
|
+
| `code.repository.merge.v1` | `repo.merge`, `repo.pr.create` | |
|
|
118
|
+
|
|
119
|
+
There is **no** `cron.v1` policy; scheduled-task tools map to `agent.session.create.v1`.
|
|
120
|
+
|
|
121
|
+
**Guardrail id note:** Hooks pass `session.create` (not a bare `cron` alias) for `CronCreate`/`CronDelete` so `cronlist` is not misclassified via a `cron*` prefix.
|
|
122
|
+
|
|
123
|
+
## Deploy / security review (internal)
|
|
124
|
+
|
|
125
|
+
| Check | Status |
|
|
126
|
+
|-------|--------|
|
|
127
|
+
| `packages/core` and `python/.../tool-pack-mapping.json` identical | Enforced in `tests/unit/test-tool-pack-mapping.sh` |
|
|
128
|
+
| Bash `resolve_policy_id_from_tool_name` fail-closed on unknown | Yes — JSON `default` is **not** applied in bash (adapters still use default) |
|
|
129
|
+
| Hooks fail-closed on unknown host tools | Claude Code + Cursor |
|
|
130
|
+
| `cronlist` → read, not session | Fixed (avoid bare `cron` prefix) |
|
|
131
|
+
| OpenClaw `cronlist` before `cron*` match | Fixed (explicit tool names only) |
|
|
132
|
+
| Published npm tarball includes mapping JSON | `python/aport_guardrails/core/tool-pack-mapping.json` in root `package.json` `files` |
|
|
133
|
+
|
|
134
|
+
**Known DRY debt (acceptable for now):** host hooks (`aport-*-hook.sh`) duplicate case lists; OpenClaw uses `tool-mapping.js`. Both must stay aligned with `tool-pack-mapping.json` when adding tools. `claudeCodeTools.ts` is documentation-only.
|
|
135
|
+
|
|
136
|
+
**Prefix caveat:** bare `agent` prefix matches any tool name starting with `agent` (e.g. hypothetical `agentic_search`). Prefer explicit host names in hooks.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Tests
|
|
141
|
+
|
|
142
|
+
| Test | Coverage |
|
|
143
|
+
|------|----------|
|
|
144
|
+
| `tests/unit/test-tool-pack-mapping.sh` | JSON resolution (Claude/OpenClaw/DeerFlow names) |
|
|
145
|
+
| `tests/unit/test-claude-code-hook.sh` | Claude hook allow/deny |
|
|
146
|
+
| `tests/unit/test-cursor-hook.sh` | Cursor hook |
|
|
147
|
+
| `tests/extensions/openclaw-aport.test.js` | `mapToolToPolicy()` |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Maintenance
|
|
152
|
+
|
|
153
|
+
When a framework adds tools:
|
|
154
|
+
|
|
155
|
+
1. Confirm the **exact** `tool_name` from host docs or a captured hook payload.
|
|
156
|
+
2. Update the **hook** (Claude/Cursor) or **openclaw-aport/tool-mapping.js** if host-specific.
|
|
157
|
+
3. Add prefixes to **`tool-pack-mapping.json`** (and sync Python copy).
|
|
158
|
+
4. Extend unit tests and this audit doc.
|
package/docs/RELEASE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Release process and version policy
|
|
2
2
|
|
|
3
|
-
**Current release:** 1.0.
|
|
3
|
+
**Current release:** 1.0.27 (see [CHANGELOG.md](../CHANGELOG.md)).
|
|
4
4
|
|
|
5
5
|
We keep **one version number** across all published packages (Node core, Python core, and every framework adapter). That avoids “core is 1.2 but CLI is 0.9” and keeps the story simple for users and support.
|
|
6
6
|
|
|
@@ -14,7 +14,10 @@ This mapping is implemented in `bin/aport-guardrail-api.sh` and `bin/aport-guard
|
|
|
14
14
|
| `read`, `file.read`, `data.file.read` | `data.file.read.v1` | API / evaluator |
|
|
15
15
|
| `write`, `file.write`, `data.file.write` | `data.file.write.v1` | API / evaluator |
|
|
16
16
|
| `mcp.tool.*`, `mcp.*` | `mcp.tool.execute.v1` | API / evaluator |
|
|
17
|
-
| `agent.session.*`, `session.create`, `session
|
|
17
|
+
| `agent.session.*`, `session.create`, `session.*`, `cron`, `sessions_spawn`, `sessions_send`, `sessions_yield`, `subagents`, `session_status` | `agent.session.create.v1` | API / evaluator |
|
|
18
|
+
| `sessions_list`, `sessions_history`, `view` | `data.file.read.v1` | API / evaluator |
|
|
19
|
+
| `websearch`, `web_search`, `webfetch`, `web_fetch` | `web.fetch.v1` | API / evaluator |
|
|
20
|
+
| `browser`, `web.browser` | `web.browser.v1` | API / evaluator |
|
|
18
21
|
| `agent.tool.*`, `tool.register`, `tool.*` | `agent.tool.register.v1` | API / evaluator |
|
|
19
22
|
| `payment.refund`, `payment.*`, `finance.payment.refund` | `finance.payment.refund.v1` | `external/aport-policies/finance.payment.refund.v1/` |
|
|
20
23
|
| `payment.charge`, `finance.payment.charge` | `finance.payment.charge.v1` | `external/aport-policies/finance.payment.charge.v1/` |
|
|
@@ -41,6 +44,8 @@ To add a new tool → policy mapping, edit the `case` block in:
|
|
|
41
44
|
|
|
42
45
|
and add a new pattern and policy pack ID. The policy pack must exist under `external/aport-policies/<pack_id>/` (or in local-overrides / API).
|
|
43
46
|
|
|
47
|
+
Per-framework host tool names and hook behavior: [FRAMEWORK_TOOL_MAPPING_AUDIT.md](FRAMEWORK_TOOL_MAPPING_AUDIT.md).
|
|
48
|
+
|
|
44
49
|
## Reference
|
|
45
50
|
|
|
46
51
|
- OAP spec: `external/aport-spec/`
|
|
@@ -71,16 +71,20 @@ This command intentionally runs the same supported installer flow (`npx @aporthq
|
|
|
71
71
|
|
|
72
72
|
| Claude Code tool | APort policy | Default |
|
|
73
73
|
|--------------------|---------------------------|----------|
|
|
74
|
-
| Bash
|
|
75
|
-
| Read,
|
|
76
|
-
|
|
|
74
|
+
| Bash, PowerShell, Monitor | system.command.execute.v1 | Enforce |
|
|
75
|
+
| Read, ReadFile, SemanticSearch (with `file_path`) | data.file.read.v1 | **Enforce** (sensitive paths blocked; API/local) |
|
|
76
|
+
| Glob, Grep, LSP, ListMcpResourcesTool, ReadMcpResourceTool, ToolSearch, WaitForMcpServers, TaskGet, TaskList, TodoRead | data.file.read.v1 | Allow without evaluator (no single path) |
|
|
77
|
+
| Write, Edit, MultiEdit, NotebookEdit, TodoWrite, ShareOnboardingGuide | data.file.write.v1 | Enforce |
|
|
77
78
|
| WebSearch, WebFetch | web.fetch.v1 | Enforce |
|
|
78
79
|
| Browser | web.browser.v1 | Enforce |
|
|
79
|
-
| Task
|
|
80
|
+
| Agent, Task, TaskCreate, TaskUpdate, TaskStop, Skill, EnterWorktree, ExitWorktree, SendMessage, TeamCreate, TeamDelete, RemoteTrigger | agent.session.create.v1 | Enforce |
|
|
81
|
+
| CronCreate, CronDelete | agent.session.create.v1 | Enforce |
|
|
80
82
|
| mcp__<server>__<tool> | mcp.tool.execute.v1 | Enforce |
|
|
81
83
|
| **Unknown tool** | — | **Denied (fail-closed)** |
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
Permission-rule specifiers such as `Agent(Explore)` are stripped before mapping (the hook receives `Agent(Explore)` and normalizes to `agent`).
|
|
86
|
+
|
|
87
|
+
Path-based **Read** tools call the guardrail with only `file_path` in context (not full file bodies). **Glob/Grep/LS** and similar tools still allow without an evaluator call when no single `file_path` is present.
|
|
84
88
|
|
|
85
89
|
---
|
|
86
90
|
|
|
@@ -105,14 +105,18 @@ guardrails:
|
|
|
105
105
|
|
|
106
106
|
## Tool-to-policy mapping
|
|
107
107
|
|
|
108
|
-
| DeerFlow tool | OAP policy pack |
|
|
109
|
-
|
|
110
|
-
| `bash
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `present_file`, `view_image` | `data.file.read.v1` |
|
|
114
|
-
| `
|
|
115
|
-
|
|
|
108
|
+
| DeerFlow tool | OAP policy pack | Passport capability |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `bash` | `system.command.execute.v1` | `system.command.execute` |
|
|
111
|
+
| `write_file`, `str_replace` | `data.file.write.v1` | `data.file.write` |
|
|
112
|
+
| `web_search`, `web_fetch`, `image_search` | `web.fetch.v1` | `web.fetch` |
|
|
113
|
+
| `read_file`, `ls`, `present_file`, `view_image` | `data.file.read.v1` | `data.file.read` |
|
|
114
|
+
| `git.create_pr`, `git.*` | `code.repository.merge.v1` | `repo.merge` / `repo.pr.create` |
|
|
115
|
+
| `messaging.send`, `message.*` | `messaging.message.send.v1` | `messaging.message.send` |
|
|
116
|
+
| `ask_clarification`, `task` | `agent.session.create.v1` | `agent.session.create` |
|
|
117
|
+
| MCP tools (`mcp.*`, dynamic names) | `mcp.tool.execute.v1` | `mcp.tool.execute` |
|
|
118
|
+
|
|
119
|
+
Mapping is implemented in `packages/core/src/core/tool-pack-mapping.json` (via `tool_to_pack_id()`). DeerFlow passes the raw tool name from each tool invocation.
|
|
116
120
|
|
|
117
121
|
## Evaluation modes
|
|
118
122
|
|
|
@@ -152,6 +152,19 @@ function allowByList(value, list, matcher) {
|
|
|
152
152
|
return list.some((entry) => matcher(value, entry));
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function isDefaultSensitiveReadPath(filePath) {
|
|
156
|
+
const value = String(filePath).toLowerCase();
|
|
157
|
+
return /(^|\/)\.env/.test(value) ||
|
|
158
|
+
/(^|\/)\.aws\//.test(value) ||
|
|
159
|
+
/(^|\/)\.ssh\//.test(value) ||
|
|
160
|
+
value.includes("credentials") ||
|
|
161
|
+
/(^|\/)id_(rsa|dsa|ecdsa|ed25519)/.test(value) ||
|
|
162
|
+
/\.(pem|key)$/.test(value) ||
|
|
163
|
+
value.includes("password") ||
|
|
164
|
+
/(^|\/)\.gnupg\//.test(value) ||
|
|
165
|
+
/(^|\/)\.kube\//.test(value);
|
|
166
|
+
}
|
|
167
|
+
|
|
155
168
|
function makeDeny(baseParams, code, message) {
|
|
156
169
|
return buildDecision({ allow: false, code, message, ...baseParams });
|
|
157
170
|
}
|
|
@@ -267,6 +280,10 @@ export function evaluateLocalDecision({ policyName, context, passportFile }) {
|
|
|
267
280
|
|
|
268
281
|
if (policyName === "data.file.read.v1") {
|
|
269
282
|
const filePath = String(context.file_path ?? context.path ?? "");
|
|
283
|
+
if (isDefaultSensitiveReadPath(filePath)) {
|
|
284
|
+
return makeDeny(params, "oap.blocked_pattern", "File path matches default sensitive read pattern");
|
|
285
|
+
}
|
|
286
|
+
|
|
270
287
|
if (!allowByList(filePath, limits.allowed_paths, (value, pattern) => value.startsWith(pattern) || pattern === "*")) {
|
|
271
288
|
return makeDeny(params, "oap.path_not_allowed", `File path '${filePath}' is not in allowed list`);
|
|
272
289
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-aport",
|
|
3
3
|
"name": "APort Guardrails",
|
|
4
4
|
"description": "Deterministic pre-action authorization via APort policy enforcement. Registers before_tool_call to block disallowed tools.",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.27",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aporthq/openclaw-aport",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.27",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@aporthq/openclaw-aport",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.27",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@types/node": "^18.0.0",
|
|
@@ -63,7 +63,9 @@ export function mapToolToPolicy(toolName, params) {
|
|
|
63
63
|
if (tool.match(/exec\.(run|shell)/)) return "system.command.execute.v1";
|
|
64
64
|
if (tool.startsWith("exec.")) return "system.command.execute.v1";
|
|
65
65
|
if (tool.startsWith("system.command.")) return "system.command.execute.v1";
|
|
66
|
-
if (tool === "bash" || tool === "shell" || tool === "command"
|
|
66
|
+
if (tool === "bash" || tool === "shell" || tool === "command" || tool === "powershell" || tool === "monitor") {
|
|
67
|
+
return "system.command.execute.v1";
|
|
68
|
+
}
|
|
67
69
|
|
|
68
70
|
if (tool === "message") {
|
|
69
71
|
return MESSAGE_SEND_ACTIONS.has(readAction(params)) ? "messaging.message.send.v1" : null;
|
|
@@ -81,9 +83,10 @@ export function mapToolToPolicy(toolName, params) {
|
|
|
81
83
|
return "messaging.message.send.v1";
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
if (tool === "read") return "data.file.read.v1";
|
|
86
|
+
if (tool === "read" || tool === "view") return "data.file.read.v1";
|
|
85
87
|
if (tool.startsWith("file.read")) return "data.file.read.v1";
|
|
86
88
|
if (tool.startsWith("data.file.read")) return "data.file.read.v1";
|
|
89
|
+
if (tool === "sessions_list" || tool === "sessions_history") return "data.file.read.v1";
|
|
87
90
|
if (tool === "write" || tool === "edit") return "data.file.write.v1";
|
|
88
91
|
if (tool === "multiedit" || tool === "notebookedit") return "data.file.write.v1";
|
|
89
92
|
if (tool === "glob" || tool === "ls" || tool === "grep" || tool === "toolsearch") {
|
|
@@ -91,9 +94,35 @@ export function mapToolToPolicy(toolName, params) {
|
|
|
91
94
|
}
|
|
92
95
|
if (tool === "todoread") return "data.file.read.v1";
|
|
93
96
|
if (tool === "todowrite") return "data.file.write.v1";
|
|
94
|
-
if (tool === "taskget" || tool === "tasklist" || tool === "taskoutput"
|
|
97
|
+
if (tool === "taskget" || tool === "tasklist" || tool === "taskoutput" || tool === "cronlist") {
|
|
98
|
+
return "data.file.read.v1";
|
|
99
|
+
}
|
|
100
|
+
if (
|
|
101
|
+
tool === "agent" ||
|
|
102
|
+
tool === "task" ||
|
|
103
|
+
tool === "taskcreate" ||
|
|
104
|
+
tool === "taskupdate" ||
|
|
105
|
+
tool === "taskstop" ||
|
|
106
|
+
tool === "skill" ||
|
|
107
|
+
tool === "enterworktree" ||
|
|
108
|
+
tool === "exitworktree" ||
|
|
109
|
+
tool === "subagent" ||
|
|
110
|
+
tool === "subagentstart" ||
|
|
111
|
+
tool === "sendmessage" ||
|
|
112
|
+
tool === "teamcreate" ||
|
|
113
|
+
tool === "teamdelete" ||
|
|
114
|
+
tool === "remotetrigger" ||
|
|
115
|
+
tool === "sessions_spawn" ||
|
|
116
|
+
tool === "sessions_send" ||
|
|
117
|
+
tool === "sessions_yield" ||
|
|
118
|
+
tool === "subagents" ||
|
|
119
|
+
tool === "session_status" ||
|
|
120
|
+
tool === "croncreate" ||
|
|
121
|
+
tool === "crondelete"
|
|
122
|
+
) {
|
|
123
|
+
return "agent.session.create.v1";
|
|
124
|
+
}
|
|
95
125
|
if (tool === "askuserquestion" || tool === "enterplanmode" || tool === "exitplanmode") return null;
|
|
96
|
-
if (tool === "cronlist") return "data.file.read.v1";
|
|
97
126
|
if (tool.startsWith("file.write")) return "data.file.write.v1";
|
|
98
127
|
if (tool.startsWith("file.edit")) return "data.file.write.v1";
|
|
99
128
|
if (tool.startsWith("data.file.write")) return "data.file.write.v1";
|
package/package.json
CHANGED
|
@@ -1 +1,111 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"default": "system.command.execute.v1",
|
|
3
|
+
"rules": [
|
|
4
|
+
{ "prefixes": ["git."], "pack": "code.repository.merge.v1" },
|
|
5
|
+
{
|
|
6
|
+
"prefixes": [
|
|
7
|
+
"exec.",
|
|
8
|
+
"system.",
|
|
9
|
+
"bash",
|
|
10
|
+
"shell",
|
|
11
|
+
"command",
|
|
12
|
+
"run_command",
|
|
13
|
+
"execute_command",
|
|
14
|
+
"gateway",
|
|
15
|
+
"process",
|
|
16
|
+
"powershell",
|
|
17
|
+
"monitor"
|
|
18
|
+
],
|
|
19
|
+
"pack": "system.command.execute.v1"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"prefixes": ["message.", "messaging.", "sms", "whatsapp", "slack", "email"],
|
|
23
|
+
"pack": "messaging.message.send.v1"
|
|
24
|
+
},
|
|
25
|
+
{ "prefixes": ["mcp."], "pack": "mcp.tool.execute.v1" },
|
|
26
|
+
{
|
|
27
|
+
"prefixes": [
|
|
28
|
+
"agent.session.",
|
|
29
|
+
"session.",
|
|
30
|
+
"sessions.",
|
|
31
|
+
"sessions_spawn",
|
|
32
|
+
"sessions_send",
|
|
33
|
+
"sessions_yield",
|
|
34
|
+
"subagents",
|
|
35
|
+
"session_status",
|
|
36
|
+
"croncreate",
|
|
37
|
+
"crondelete",
|
|
38
|
+
"taskcreate",
|
|
39
|
+
"taskupdate",
|
|
40
|
+
"taskstop",
|
|
41
|
+
"subagent",
|
|
42
|
+
"subagentstart",
|
|
43
|
+
"skill",
|
|
44
|
+
"enterworktree",
|
|
45
|
+
"exitworktree",
|
|
46
|
+
"sendmessage",
|
|
47
|
+
"teamcreate",
|
|
48
|
+
"teamdelete",
|
|
49
|
+
"remotetrigger",
|
|
50
|
+
"schedulewakeup"
|
|
51
|
+
],
|
|
52
|
+
"pack": "agent.session.create.v1"
|
|
53
|
+
},
|
|
54
|
+
{ "prefixes": ["agent.tool."], "substrings": ["tool.register"], "pack": "agent.tool.register.v1" },
|
|
55
|
+
{ "prefixes": ["agent"], "pack": "agent.session.create.v1" },
|
|
56
|
+
{ "substrings": ["payment.refund", "finance.payment.refund"], "pack": "finance.payment.refund.v1" },
|
|
57
|
+
{ "substrings": ["payment.charge", "finance.payment.charge"], "pack": "finance.payment.charge.v1" },
|
|
58
|
+
{ "prefixes": ["database.", "data.export"], "pack": "data.export.create.v1" },
|
|
59
|
+
{
|
|
60
|
+
"prefixes": [
|
|
61
|
+
"write_file",
|
|
62
|
+
"str_replace",
|
|
63
|
+
"write",
|
|
64
|
+
"edit",
|
|
65
|
+
"multiedit",
|
|
66
|
+
"notebookedit",
|
|
67
|
+
"file.write",
|
|
68
|
+
"file.edit",
|
|
69
|
+
"data.file.write",
|
|
70
|
+
"shareonboardingguide"
|
|
71
|
+
],
|
|
72
|
+
"pack": "data.file.write.v1"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"prefixes": [
|
|
76
|
+
"read_file",
|
|
77
|
+
"ls",
|
|
78
|
+
"present_file",
|
|
79
|
+
"view_image",
|
|
80
|
+
"view",
|
|
81
|
+
"sessions_list",
|
|
82
|
+
"sessions_history",
|
|
83
|
+
"cronlist",
|
|
84
|
+
"read",
|
|
85
|
+
"glob",
|
|
86
|
+
"grep",
|
|
87
|
+
"lsp",
|
|
88
|
+
"file.read",
|
|
89
|
+
"data.file.read",
|
|
90
|
+
"listmcpresourcestool",
|
|
91
|
+
"readmcpresourcetool",
|
|
92
|
+
"toolsearch",
|
|
93
|
+
"waitformcpservers"
|
|
94
|
+
],
|
|
95
|
+
"pack": "data.file.read.v1"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"prefixes": [
|
|
99
|
+
"web_search",
|
|
100
|
+
"web_fetch",
|
|
101
|
+
"websearch",
|
|
102
|
+
"image_search",
|
|
103
|
+
"webfetch",
|
|
104
|
+
"web.search",
|
|
105
|
+
"web.fetch"
|
|
106
|
+
],
|
|
107
|
+
"pack": "web.fetch.v1"
|
|
108
|
+
},
|
|
109
|
+
{ "prefixes": ["browser", "web.browser"], "pack": "web.browser.v1" }
|
|
110
|
+
]
|
|
111
|
+
}
|