@aporthq/aport-agent-guardrails 1.0.19 β 1.0.21
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 -15
- package/bin/agent-guardrails +57 -17
- package/bin/aport-create-passport.sh +1 -1
- package/bin/aport-guardrail-api.sh +8 -35
- package/bin/aport-guardrail-bash.sh +18 -53
- package/bin/aport-resolve-paths.sh +2 -2
- package/bin/frameworks/generic.sh +59 -3
- package/bin/frameworks/next-steps.d/crewai-native.txt +21 -0
- package/bin/frameworks/next-steps.d/crewai.txt +7 -2
- package/bin/lib/error.sh +3 -3
- package/bin/lib/runtime-manifest.txt +17 -0
- package/bin/lib/runtime.sh +81 -0
- package/bin/lib/tool-mapping.sh +52 -0
- package/bin/lib/validation.sh +25 -15
- package/docs/FRAMEWORK_ROADMAP.md +1 -1
- package/docs/PROVIDER.md +90 -0
- package/docs/RELEASE.md +3 -3
- package/docs/development/ERROR_CODES.md +3 -3
- package/docs/frameworks/crewai.md +102 -68
- package/docs/frameworks/deerflow.md +1 -1
- package/docs/frameworks/openclaw.md +51 -22
- package/extensions/openclaw-aport/CHANGELOG.md +5 -1
- 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/external/aport-spec/LICENSE +1 -1
- package/external/aport-spec/README.md +1 -0
- package/external/aport-spec/conformance/src/ed25519.ts +1 -1
- package/external/aport-spec/oap/CHANGELOG.md +1 -1
- package/external/aport-spec/oap/conformance.md +2 -2
- package/external/aport-spec/oap/oap-spec.md +2 -2
- package/external/aport-spec/oap/security.md +4 -4
- package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +1 -1
- package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +1 -1
- package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +1 -1
- package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +1 -1
- package/external/aport-spec/oap/vc/tools/src/index.ts +2 -2
- package/external/aport-spec/oap/vc/tools/test-simple.js +2 -2
- package/external/aport-spec/oap/vc/vc-mapping.md +4 -4
- package/external/aport-spec/oap/well-known-schema.json +85 -0
- package/external/aport-spec/well-known.md +203 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -89,23 +89,24 @@ The security concern is that agent tools and skills can execute sensitive action
|
|
|
89
89
|
|
|
90
90
|
## π Supported frameworks
|
|
91
91
|
|
|
92
|
-
**APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI/DeerFlow):** run the same CLI for the wizard and config, then install the
|
|
92
|
+
**APort Agent Guardrail** adapters and providers are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI/DeerFlow):** run the same CLI for the wizard and config, then install the Python package shown in the framework doc.
|
|
93
93
|
|
|
94
|
-
**Two ways to use APort:** (1) **Guardrails (CLI/setup)** β run the installer to create your passport and config; (2) **Core (library)** β use the
|
|
94
|
+
**Two ways to use APort:** (1) **Guardrails (CLI/setup)** β run the installer to create your passport and config; (2) **Core (library)** β use the `OAPGuardrailProvider` ([docs/PROVIDER.md](docs/PROVIDER.md)) in your app so each tool call is verified. One provider per language (Python + TypeScript), works with any framework. Framework docs: [OpenClaw](docs/frameworks/openclaw.md), [Cursor](docs/frameworks/cursor.md), [Claude Code](docs/frameworks/claude-code.md), [LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [DeerFlow](docs/frameworks/deerflow.md), [n8n](docs/frameworks/n8n.md).
|
|
95
95
|
|
|
96
96
|
**CLI-supported frameworks:** `openclaw`, `langchain`, `crewai`, `cursor`, `claude-code`, `deerflow`, `n8n`. OpenClaw/Cursor/Claude Code include runtime-specific integration scripts; DeerFlow/LangChain/CrewAI use framework docs plus generic setup output from the CLI. See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
|
|
97
97
|
|
|
98
98
|
| Framework | Doc | Integration | Install |
|
|
99
99
|
|-----------|-----|--------------|--------|
|
|
100
|
-
| **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | `
|
|
100
|
+
| **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | Native `GuardrailProvider` or plugin | `npm i @aporthq/aport-agent-guardrails-core && npx @aporthq/aport-agent-guardrails openclaw` |
|
|
101
101
|
| **Cursor** | [docs/frameworks/cursor.md](docs/frameworks/cursor.md) | `beforeShellExecution` / `preToolUse` hooks β writes `~/.cursor/hooks.json`. **Runtime enforcement is the bash hook;** the Node package `@aporthq/aport-agent-guardrails-cursor` is a helper only (Evaluator, `getHookPath()`). | `npx @aporthq/aport-agent-guardrails cursor` |
|
|
102
102
|
| **Claude Code** | [docs/frameworks/claude-code.md](docs/frameworks/claude-code.md) | PreToolUse hook β writes `~/.claude/settings.json` (Claude Code format; not Cursor). | `npx @aporthq/aport-agent-guardrails claude-code` |
|
|
103
103
|
| **LangChain / LangGraph** | [docs/frameworks/langchain.md](docs/frameworks/langchain.md) | **Python:** `APortCallback` (`on_tool_start`) | `npx @aporthq/aport-agent-guardrails langchain` then `pip install aport-agent-guardrails-langchain` + `aport-langchain setup` |
|
|
104
|
-
| **CrewAI** | [docs/frameworks/crewai.md](docs/frameworks/crewai.md) | **Python:**
|
|
104
|
+
| **CrewAI** | [docs/frameworks/crewai.md](docs/frameworks/crewai.md) | **Python:** released hook adapter by default; native `GuardrailProvider` mode for CrewAI builds with native provider support | `npx @aporthq/aport-agent-guardrails crewai` then `pip install aport-agent-guardrails-crewai` + `aport-crewai setup` |
|
|
105
105
|
| **DeerFlow** | [docs/frameworks/deerflow.md](docs/frameworks/deerflow.md) | **Python:** generic OAP provider wiring in DeerFlow config | `npx @aporthq/aport-agent-guardrails deerflow` then follow printed `uv`/config steps |
|
|
106
106
|
| **n8n** | [docs/frameworks/n8n.md](docs/frameworks/n8n.md) | *Coming soon* β custom node and runtime in progress | β |
|
|
107
|
+
| **VoltAgent** | [VoltAgent PR #1171](https://github.com/VoltAgent/voltagent/pull/1171) | *In progress* β pluggable `GuardrailProvider` interface landing upstream in `@voltagent/core` | β |
|
|
107
108
|
|
|
108
|
-
Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when prompted). OpenClaw can also use the full installer flow. **For LangChain and
|
|
109
|
+
Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when prompted). OpenClaw can also use the full installer flow. **For LangChain, CrewAI, and DeerFlow, the CLI writes config and installs the local runtime into the framework config directory; then install the Python package and wire the provider/callback shown in the framework doc.** **Python** packages are on PyPI; **Node** packages are on npm (same version as the CLI).
|
|
109
110
|
|
|
110
111
|
**Passport path:** Each framework has its own **default** passport path (where that framework stores data): e.g. Cursor β `~/.cursor/aport/passport.json`, OpenClaw β `~/.openclaw/aport/passport.json`, LangChain β `~/.aport/langchain/aport/passport.json`. The passport wizardβs **first question** is βPassport file path [default]:β β press Enter for the framework default or type a different path. In non-interactive mode (e.g. CI) use **`--output /path/to/passport.json`** to choose the path. Roadmap: [docs/FRAMEWORK_ROADMAP.md](docs/FRAMEWORK_ROADMAP.md).
|
|
111
112
|
|
|
@@ -125,15 +126,15 @@ npx @aporthq/aport-agent-guardrails
|
|
|
125
126
|
# or: npx @aporthq/aport-agent-guardrails openclaw | cursor | claude-code | langchain | crewai | deerflow | n8n
|
|
126
127
|
```
|
|
127
128
|
|
|
128
|
-
**Python (LangChain or
|
|
129
|
+
**Python (LangChain, CrewAI, or DeerFlow):** Run the wizard via the Node command above, then install the Python package and follow the printed next steps. Or use the Python CLI to see the exact commands:
|
|
129
130
|
```bash
|
|
130
131
|
pip install aport-agent-guardrails
|
|
131
|
-
aport setup --framework=langchain
|
|
132
|
-
# or --framework=crewai
|
|
132
|
+
aport setup --framework=langchain
|
|
133
|
+
# or --framework=crewai / deerflow
|
|
133
134
|
```
|
|
134
|
-
Then run the printed `npx` command to create passport and config, and the printed
|
|
135
|
+
Then run the printed `npx` command to create passport and config, and the printed Python integration step for your framework.
|
|
135
136
|
|
|
136
|
-
This runs the **passport wizard** and writes config for your framework. Follow the **next steps** printed at the end (e.g. restart Cursor; or for
|
|
137
|
+
This runs the **passport wizard** and writes config for your framework. Follow the **next steps** printed at the end (e.g. restart Cursor; or for CrewAI: by default install `aport-agent-guardrails-crewai` for released CrewAI, or opt into native-provider mode if your CrewAI build supports it).
|
|
137
138
|
|
|
138
139
|
**2. Hosted passport (optional)** β If you already have an agent_id from [aport.io](https://aport.io), use it to skip the wizard: `npx @aporthq/aport-agent-guardrails openclaw <agent_id>`. See [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
|
|
139
140
|
|
|
@@ -147,7 +148,7 @@ aport-guardrail system.command.execute '{"command":"rm -rf /"}' # DENY (blocked
|
|
|
147
148
|
```
|
|
148
149
|
*(If you use `npx` without `-g`, run `npx aport-guardrail ...`.)*
|
|
149
150
|
|
|
150
|
-
**Python:** Use the guardrail in your app (e.g. add `APortCallback()` to your LangChain agent,
|
|
151
|
+
**Python:** Use the guardrail in your app (e.g. add `APortCallback()` to your LangChain agent, use `register_aport_guardrail()` for released CrewAI, or `enable_guardrail(OAPGuardrailProvider(...))` for CrewAI builds with native provider support). The guardrail runs on every tool call. To test allow/deny from the shell without Node, use `npx aport-guardrail ...` as above, or see your framework doc for in-app testing.
|
|
151
152
|
|
|
152
153
|
**Check passport status and audit:**
|
|
153
154
|
|
|
@@ -397,14 +398,14 @@ See [Verification methods](docs/VERIFICATION_METHODS.md) for a detailed comparis
|
|
|
397
398
|
| `aport` | OpenClaw one-command setup (passport + plugin + wrappers). Optional: `aport <agent_id>` for hosted passport. |
|
|
398
399
|
| `aport-guardrail` | Run guardrail check from the CLI (e.g. `aport-guardrail system.command.execute '{"command":"ls"}'`). Uses passport from your framework config dir. |
|
|
399
400
|
|
|
400
|
-
**Python:** After `pip install aport-agent-guardrails` you get `aport` (setup helper). For LangChain or CrewAI, install the
|
|
401
|
+
**Python:** After `pip install aport-agent-guardrails` you get `aport` (setup helper). For LangChain or CrewAI, install the framework package and setup:
|
|
401
402
|
|
|
402
403
|
| Command | Purpose |
|
|
403
404
|
|--------|---------|
|
|
404
405
|
| `aport setup --framework=langchain` | Print next-step commands (npx wizard, then `pip install aport-agent-guardrails-langchain`, `aport-langchain setup`). |
|
|
405
|
-
| `aport setup --framework=crewai` |
|
|
406
|
+
| `aport setup --framework=crewai` | Default released CrewAI path: bootstrap config/runtime, then use `pip install aport-agent-guardrails-crewai` and `aport-crewai setup`. |
|
|
407
|
+
| `aport setup --framework=crewai --integration-mode=native` | Native CrewAI path: bootstrap config/runtime, then use `uv add aport-agent-guardrails` and `OAPGuardrailProvider`. |
|
|
406
408
|
| `aport-langchain setup` | LangChain config and wizard (after installing `aport-agent-guardrails-langchain`). |
|
|
407
|
-
| `aport-crewai setup` | CrewAI config and wizard (after installing `aport-agent-guardrails-crewai`). |
|
|
408
409
|
|
|
409
410
|
Use the framework-specific doc for where config and passport live and for any extra steps (e.g. Cursor: restart IDE; LangChain/CrewAI: add callback/hook in code).
|
|
410
411
|
|
|
@@ -429,7 +430,7 @@ Use the framework-specific doc for where config and passport live and for any ex
|
|
|
429
430
|
| β [Cursor](docs/frameworks/cursor.md) | beforeShellExecution / preToolUse hooks, `~/.cursor/hooks.json` |
|
|
430
431
|
| β [Claude Code](docs/frameworks/claude-code.md) | PreToolUse hook, `~/.claude/settings.json` |
|
|
431
432
|
| β [LangChain / LangGraph](docs/frameworks/langchain.md) | `APortCallback` handler |
|
|
432
|
-
| β [CrewAI](docs/frameworks/crewai.md) |
|
|
433
|
+
| β [CrewAI](docs/frameworks/crewai.md) | Released hook adapter by default; native provider mode when available |
|
|
433
434
|
| β [DeerFlow](docs/frameworks/deerflow.md) | Generic provider wiring via DeerFlow `config.yaml` |
|
|
434
435
|
| β [n8n](docs/frameworks/n8n.md) | Custom node, branch on allow/deny |
|
|
435
436
|
| [Framework roadmap](docs/FRAMEWORK_ROADMAP.md) | Support status and roadmap |
|
package/bin/agent-guardrails
CHANGED
|
@@ -28,9 +28,22 @@ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
|
|
|
28
28
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
29
29
|
FRAMEWORKS_DIR="$SCRIPT_DIR/frameworks"
|
|
30
30
|
LIB_DIR="$SCRIPT_DIR/lib"
|
|
31
|
+
SUPPORTED_FRAMEWORKS=(openclaw langchain crewai cursor claude-code deerflow n8n)
|
|
32
|
+
|
|
33
|
+
framework_supported() {
|
|
34
|
+
local candidate="${1:-}"
|
|
35
|
+
local supported
|
|
36
|
+
for supported in "${SUPPORTED_FRAMEWORKS[@]}"; do
|
|
37
|
+
if [[ "$candidate" == "$supported" ]]; then
|
|
38
|
+
return 0
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
return 1
|
|
42
|
+
}
|
|
31
43
|
|
|
32
44
|
# Parse --framework= and -f (skip detection when set)
|
|
33
45
|
framework=""
|
|
46
|
+
integration_mode=""
|
|
34
47
|
REST=()
|
|
35
48
|
while [[ $# -gt 0 ]]; do
|
|
36
49
|
case "$1" in
|
|
@@ -47,6 +60,19 @@ while [[ $# -gt 0 ]]; do
|
|
|
47
60
|
exit 1
|
|
48
61
|
fi
|
|
49
62
|
;;
|
|
63
|
+
--integration-mode=*)
|
|
64
|
+
integration_mode="${1#--integration-mode=}"
|
|
65
|
+
shift
|
|
66
|
+
;;
|
|
67
|
+
--integration-mode)
|
|
68
|
+
if [[ $# -gt 1 ]]; then
|
|
69
|
+
integration_mode="$2"
|
|
70
|
+
shift 2
|
|
71
|
+
else
|
|
72
|
+
echo "[aport] ERROR: --integration-mode requires a value (compat or native)" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
;;
|
|
50
76
|
*)
|
|
51
77
|
REST+=("$1")
|
|
52
78
|
shift
|
|
@@ -59,16 +85,11 @@ if [[ -z "$framework" ]] && [[ ${#REST[@]} -gt 0 ]]; then
|
|
|
59
85
|
first_arg="${REST[0]}"
|
|
60
86
|
# Check if first arg looks like a framework name (lowercase alphanumeric + hyphen)
|
|
61
87
|
if [[ "$first_arg" =~ ^[a-z0-9-]+$ ]]; then
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# Remove framework from REST so remaining args are pass-through
|
|
68
|
-
REST=("${REST[@]:1}")
|
|
69
|
-
break
|
|
70
|
-
fi
|
|
71
|
-
done
|
|
88
|
+
if framework_supported "$first_arg"; then
|
|
89
|
+
framework="$first_arg"
|
|
90
|
+
# Remove framework from REST so remaining args are pass-through
|
|
91
|
+
REST=("${REST[@]:1}")
|
|
92
|
+
fi
|
|
72
93
|
fi
|
|
73
94
|
fi
|
|
74
95
|
|
|
@@ -98,7 +119,7 @@ if [[ -z "$framework" ]]; then
|
|
|
98
119
|
echo " βββββββββββββββββββββββββββββββββββββββββ"
|
|
99
120
|
echo " Multiple frameworks detected: $detected_list"
|
|
100
121
|
echo " Choose one: openclaw | langchain | crewai | cursor | claude-code | deerflow | n8n"
|
|
101
|
-
echo " Example: npx @aporthq/agent-guardrails --framework=openclaw"
|
|
122
|
+
echo " Example: npx @aporthq/aport-agent-guardrails --framework=openclaw"
|
|
102
123
|
echo ""
|
|
103
124
|
read -p " Framework [${framework:-openclaw}]: " choice
|
|
104
125
|
framework="${choice:-${framework:-openclaw}}"
|
|
@@ -121,8 +142,8 @@ if [[ -z "$framework" ]]; then
|
|
|
121
142
|
echo " βββββββββββββββββββββββββββββββββββββββββ"
|
|
122
143
|
echo " No framework detected in current directory."
|
|
123
144
|
echo " Choose: openclaw | langchain | crewai | cursor | claude-code | deerflow | n8n"
|
|
124
|
-
echo " Example: npx @aporthq/agent-guardrails openclaw"
|
|
125
|
-
echo " npx @aporthq/agent-guardrails --framework=langchain"
|
|
145
|
+
echo " Example: npx @aporthq/aport-agent-guardrails openclaw"
|
|
146
|
+
echo " npx @aporthq/aport-agent-guardrails --framework=langchain"
|
|
126
147
|
echo ""
|
|
127
148
|
read -p " Framework [openclaw]: " framework
|
|
128
149
|
framework="${framework:-openclaw}"
|
|
@@ -136,6 +157,29 @@ if [[ ! "$framework" =~ ^[a-z0-9-]+$ ]]; then
|
|
|
136
157
|
exit 1
|
|
137
158
|
fi
|
|
138
159
|
|
|
160
|
+
if ! framework_supported "$framework"; then
|
|
161
|
+
echo "[aport] ERROR: Unknown or unsupported framework: $framework" >&2
|
|
162
|
+
echo " Supported: ${SUPPORTED_FRAMEWORKS[*]}" >&2
|
|
163
|
+
exit 1
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
echo "[aport] Selected framework: $framework" >&2
|
|
167
|
+
|
|
168
|
+
if [[ -n "$integration_mode" ]]; then
|
|
169
|
+
case "$integration_mode" in
|
|
170
|
+
compat|native) ;;
|
|
171
|
+
*)
|
|
172
|
+
echo "[aport] ERROR: Unsupported integration mode: $integration_mode (expected compat or native)" >&2
|
|
173
|
+
exit 1
|
|
174
|
+
;;
|
|
175
|
+
esac
|
|
176
|
+
if [[ "$framework" != "crewai" ]]; then
|
|
177
|
+
echo "[aport] ERROR: --integration-mode is only supported for CrewAI" >&2
|
|
178
|
+
exit 1
|
|
179
|
+
fi
|
|
180
|
+
REST+=("--integration-mode=$integration_mode")
|
|
181
|
+
fi
|
|
182
|
+
|
|
139
183
|
script="$FRAMEWORKS_DIR/${framework}.sh"
|
|
140
184
|
|
|
141
185
|
# n8n: custom node not yet available; warn before running wizard-only script
|
|
@@ -160,7 +204,3 @@ if [[ -x "$generic_script" ]]; then
|
|
|
160
204
|
export APORT_FRAMEWORK="$framework"
|
|
161
205
|
exec "$generic_script" ${REST+"${REST[@]}"}
|
|
162
206
|
fi
|
|
163
|
-
|
|
164
|
-
echo "[aport] ERROR: Unknown or unsupported framework: $framework" >&2
|
|
165
|
-
echo " Supported: openclaw, langchain, crewai, cursor, claude-code, deerflow, n8n" >&2
|
|
166
|
-
exit 1
|
|
@@ -14,6 +14,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
14
14
|
# Resolve paths: config_dir/aport/ (new) or config_dir (legacy); same as bash guardrail
|
|
15
15
|
# shellcheck source=bin/aport-resolve-paths.sh
|
|
16
16
|
. "${SCRIPT_DIR}/bin/aport-resolve-paths.sh"
|
|
17
|
+
# shellcheck source=bin/lib/tool-mapping.sh
|
|
18
|
+
. "${SCRIPT_DIR}/bin/lib/tool-mapping.sh"
|
|
17
19
|
|
|
18
20
|
NODE_EVALUATOR="$SCRIPT_DIR/src/evaluator.js"
|
|
19
21
|
|
|
@@ -48,41 +50,12 @@ if [ -z "$APORT_AGENT_ID" ] && [ ! -f "$PASSPORT_FILE" ]; then
|
|
|
48
50
|
exit 1
|
|
49
51
|
fi
|
|
50
52
|
|
|
51
|
-
# Map tool to policy pack ID
|
|
52
|
-
POLICY_ID=""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
exec.run | exec.* | system.command.* | system.*)
|
|
58
|
-
POLICY_ID="system.command.execute.v1"
|
|
59
|
-
;;
|
|
60
|
-
message.send | message.* | messaging.*)
|
|
61
|
-
POLICY_ID="messaging.message.send.v1"
|
|
62
|
-
;;
|
|
63
|
-
mcp.tool.* | mcp.*)
|
|
64
|
-
POLICY_ID="mcp.tool.execute.v1"
|
|
65
|
-
;;
|
|
66
|
-
agent.session.* | session.create | session.*)
|
|
67
|
-
POLICY_ID="agent.session.create.v1"
|
|
68
|
-
;;
|
|
69
|
-
agent.tool.* | tool.register | tool.*)
|
|
70
|
-
POLICY_ID="agent.tool.register.v1"
|
|
71
|
-
;;
|
|
72
|
-
payment.refund | payment.* | finance.payment.refund)
|
|
73
|
-
POLICY_ID="finance.payment.refund.v1"
|
|
74
|
-
;;
|
|
75
|
-
payment.charge | finance.payment.charge)
|
|
76
|
-
POLICY_ID="finance.payment.charge.v1"
|
|
77
|
-
;;
|
|
78
|
-
database.write | database.* | data.export)
|
|
79
|
-
POLICY_ID="data.export.create.v1"
|
|
80
|
-
;;
|
|
81
|
-
*)
|
|
82
|
-
echo "Error: Tool '$TOOL_NAME' is not mapped to a policy pack" >&2
|
|
83
|
-
exit 1
|
|
84
|
-
;;
|
|
85
|
-
esac
|
|
53
|
+
# Map tool to policy pack ID from the shared JSON source of truth.
|
|
54
|
+
POLICY_ID="$(resolve_policy_id_from_tool_name "$TOOL_NAME" || true)"
|
|
55
|
+
if [[ -z "$POLICY_ID" ]]; then
|
|
56
|
+
echo "Error: Tool '$TOOL_NAME' is not mapped to a policy pack" >&2
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
86
59
|
|
|
87
60
|
# Call Node.js evaluator with API
|
|
88
61
|
if [ -n "$DEBUG_APORT" ]; then
|
|
@@ -13,6 +13,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
13
13
|
# Source validation library for input sanitization
|
|
14
14
|
# shellcheck source=bin/lib/validation.sh
|
|
15
15
|
. "${SCRIPT_DIR}/bin/lib/validation.sh"
|
|
16
|
+
# shellcheck source=bin/lib/tool-mapping.sh
|
|
17
|
+
. "${SCRIPT_DIR}/bin/lib/tool-mapping.sh"
|
|
16
18
|
|
|
17
19
|
# Get script directory to find submodules (external/ per GIT_SUBMODULES_EXPLAINED.md)
|
|
18
20
|
POLICIES_DIR="$SCRIPT_DIR/external/aport-policies"
|
|
@@ -94,11 +96,14 @@ write_decision() {
|
|
|
94
96
|
local deny_message="${4:-Policy evaluation failed}"
|
|
95
97
|
|
|
96
98
|
local decision_id=$(uuidgen 2> /dev/null || echo "local-$(date +%s)")
|
|
97
|
-
local passport_id=$(jq -r '.passport_id // "unknown"' "$PASSPORT_FILE")
|
|
99
|
+
local passport_id=$(jq -r '.passport_id // .agent_id // "unknown"' "$PASSPORT_FILE")
|
|
100
|
+
local agent_id=$(jq -r '.agent_id // .passport_id // "unknown"' "$PASSPORT_FILE")
|
|
98
101
|
local owner_id=$(jq -r '.owner_id // "unknown"' "$PASSPORT_FILE")
|
|
99
102
|
local assurance_level=$(jq -r '.assurance_level // "L0"' "$PASSPORT_FILE")
|
|
100
103
|
local passport_digest=$(compute_passport_digest "$PASSPORT_FILE")
|
|
101
104
|
local issued_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
105
|
+
local created_at="$issued_at"
|
|
106
|
+
local expires_in=3600
|
|
102
107
|
local expires_at=$(date -u -v+1H +%Y-%m-%dT%H:%M:%SZ 2> /dev/null || date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ)
|
|
103
108
|
|
|
104
109
|
# Build reasons array per OAP v1.0 spec
|
|
@@ -126,12 +131,15 @@ write_decision() {
|
|
|
126
131
|
--arg decision_id "$decision_id" \
|
|
127
132
|
--arg policy_id "$policy_id" \
|
|
128
133
|
--arg passport_id "$passport_id" \
|
|
134
|
+
--arg agent_id "$agent_id" \
|
|
129
135
|
--arg owner_id "$owner_id" \
|
|
130
136
|
--arg assurance_level "$assurance_level" \
|
|
131
137
|
--argjson allow "$allow" \
|
|
132
138
|
--argjson reasons "$reasons" \
|
|
133
139
|
--arg issued_at "$issued_at" \
|
|
140
|
+
--arg created_at "$created_at" \
|
|
134
141
|
--arg expires_at "$expires_at" \
|
|
142
|
+
--argjson expires_in "$expires_in" \
|
|
135
143
|
--arg passport_digest "$passport_digest" \
|
|
136
144
|
--arg prev_decision_id "$prev_decision_id" \
|
|
137
145
|
--arg prev_content_hash "$prev_content_hash" \
|
|
@@ -139,12 +147,15 @@ write_decision() {
|
|
|
139
147
|
decision_id: $decision_id,
|
|
140
148
|
policy_id: $policy_id,
|
|
141
149
|
passport_id: $passport_id,
|
|
150
|
+
agent_id: $agent_id,
|
|
142
151
|
owner_id: $owner_id,
|
|
143
152
|
assurance_level: $assurance_level,
|
|
144
153
|
allow: $allow,
|
|
145
154
|
reasons: $reasons,
|
|
146
155
|
issued_at: $issued_at,
|
|
156
|
+
created_at: $created_at,
|
|
147
157
|
expires_at: $expires_at,
|
|
158
|
+
expires_in: $expires_in,
|
|
148
159
|
passport_digest: $passport_digest,
|
|
149
160
|
signature: "local-unsigned",
|
|
150
161
|
kid: "oap:local:dev-key",
|
|
@@ -216,58 +227,12 @@ if [ "$SPEC_VERSION" != "oap/1.0" ]; then
|
|
|
216
227
|
write_decision false "unknown" "oap.passport_version_mismatch" "Passport spec version is '$SPEC_VERSION', expected 'oap/1.0'"
|
|
217
228
|
fi
|
|
218
229
|
|
|
219
|
-
# Map tool to policy pack ID
|
|
220
|
-
POLICY_ID=""
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
exec | exec.run | exec.* | system.* | bash | shell | command)
|
|
226
|
-
POLICY_ID="system.command.execute.v1"
|
|
227
|
-
;;
|
|
228
|
-
gateway | gateway.* | process | process.*)
|
|
229
|
-
# High risk operations - treat as command execution
|
|
230
|
-
POLICY_ID="system.command.execute.v1"
|
|
231
|
-
;;
|
|
232
|
-
message.send | message.* | messaging.* | sms | whatsapp | slack | email)
|
|
233
|
-
POLICY_ID="messaging.message.send.v1"
|
|
234
|
-
;;
|
|
235
|
-
read | file.read | file.read.* | data.file.read | data.file.read.*)
|
|
236
|
-
POLICY_ID="data.file.read.v1"
|
|
237
|
-
;;
|
|
238
|
-
write | edit | file.write | file.write.* | file.edit | file.edit.* | data.file.write | data.file.write.*)
|
|
239
|
-
POLICY_ID="data.file.write.v1"
|
|
240
|
-
;;
|
|
241
|
-
web_fetch | webfetch | web.fetch | web.fetch.* | web_search | websearch | web.search | web.search.*)
|
|
242
|
-
POLICY_ID="web.fetch.v1"
|
|
243
|
-
;;
|
|
244
|
-
browser | browser.* | web.browser | web.browser.*)
|
|
245
|
-
POLICY_ID="web.browser.v1"
|
|
246
|
-
;;
|
|
247
|
-
mcp.*)
|
|
248
|
-
POLICY_ID="mcp.tool.execute.v1"
|
|
249
|
-
;;
|
|
250
|
-
agent.session.* | session.create | session.* | sessions.* | sessions_spawn | sessions_send)
|
|
251
|
-
POLICY_ID="agent.session.create.v1"
|
|
252
|
-
;;
|
|
253
|
-
cron | cron.*)
|
|
254
|
-
# Scheduled tasks - treat as session management
|
|
255
|
-
POLICY_ID="agent.session.create.v1"
|
|
256
|
-
;;
|
|
257
|
-
agent.tool.* | tool.register)
|
|
258
|
-
POLICY_ID="agent.tool.register.v1"
|
|
259
|
-
;;
|
|
260
|
-
payment.refund | refund | payment.charge | charge | payment.* | finance.*)
|
|
261
|
-
POLICY_ID="finance.payment.refund.v1"
|
|
262
|
-
;;
|
|
263
|
-
database.write | database.insert | database.update | database.delete | data.export | data.export.*)
|
|
264
|
-
POLICY_ID="data.export.create.v1"
|
|
265
|
-
;;
|
|
266
|
-
*)
|
|
267
|
-
# Unknown tool - deny by default for security
|
|
268
|
-
write_decision false "unknown" "oap.unknown_capability" "Tool '$TOOL_NAME' is not mapped to a policy pack"
|
|
269
|
-
;;
|
|
270
|
-
esac
|
|
230
|
+
# Map tool to policy pack ID from the shared JSON source of truth.
|
|
231
|
+
POLICY_ID="$(resolve_policy_id_from_tool_name "$TOOL_NAME" || true)"
|
|
232
|
+
if [[ -z "$POLICY_ID" ]]; then
|
|
233
|
+
# Unknown tool - deny by default for security.
|
|
234
|
+
write_decision false "unknown" "oap.unknown_capability" "Tool '$TOOL_NAME' is not mapped to a policy pack"
|
|
235
|
+
fi
|
|
271
236
|
|
|
272
237
|
# Capability-specific context summary for audit log (command, recipient, repo/branch, file_path, etc.)
|
|
273
238
|
CONTEXT_SUMMARY=""
|
|
@@ -56,8 +56,8 @@ resolve_aport_paths() {
|
|
|
56
56
|
# 1) Explicit path set and file exists β use it (plugin or wrapper)
|
|
57
57
|
if [ -n "${OPENCLAW_PASSPORT_FILE:-}" ] && [ -f "$OPENCLAW_PASSPORT_FILE" ]; then
|
|
58
58
|
# Validate env-provided path if validator is available
|
|
59
|
-
if type
|
|
60
|
-
if !
|
|
59
|
+
if type validate_explicit_passport_path &> /dev/null; then
|
|
60
|
+
if ! validate_explicit_passport_path "$OPENCLAW_PASSPORT_FILE"; then
|
|
61
61
|
echo "[aport] WARN: OPENCLAW_PASSPORT_FILE path failed validation: $OPENCLAW_PASSPORT_FILE" >&2
|
|
62
62
|
fi
|
|
63
63
|
fi
|
|
@@ -13,23 +13,79 @@ FRAMEWORKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" && pwd)"
|
|
|
13
13
|
source "$LIB/common.sh"
|
|
14
14
|
# shellcheck source=../lib/passport.sh
|
|
15
15
|
source "$LIB/passport.sh"
|
|
16
|
+
# shellcheck source=../lib/runtime.sh
|
|
17
|
+
source "$LIB/runtime.sh"
|
|
16
18
|
# shellcheck source=../lib/config.sh
|
|
17
19
|
source "$LIB/config.sh"
|
|
18
20
|
|
|
19
21
|
framework="${APORT_FRAMEWORK:?APORT_FRAMEWORK must be set by the dispatcher}"
|
|
22
|
+
crewai_integration_mode="${APORT_CREWAI_INTEGRATION_MODE:-compat}"
|
|
23
|
+
|
|
24
|
+
FORWARD_ARGS=()
|
|
25
|
+
while [[ $# -gt 0 ]]; do
|
|
26
|
+
case "$1" in
|
|
27
|
+
--integration-mode=*)
|
|
28
|
+
crewai_integration_mode="${1#--integration-mode=}"
|
|
29
|
+
;;
|
|
30
|
+
--integration-mode)
|
|
31
|
+
if [[ $# -lt 2 ]]; then
|
|
32
|
+
log_error "--integration-mode requires compat or native"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
crewai_integration_mode="$2"
|
|
36
|
+
shift
|
|
37
|
+
;;
|
|
38
|
+
*)
|
|
39
|
+
FORWARD_ARGS+=("$1")
|
|
40
|
+
;;
|
|
41
|
+
esac
|
|
42
|
+
shift
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
case "$crewai_integration_mode" in
|
|
46
|
+
compat | native) ;;
|
|
47
|
+
*)
|
|
48
|
+
log_error "Unsupported CrewAI integration mode: $crewai_integration_mode"
|
|
49
|
+
exit 1
|
|
50
|
+
;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
if [[ "$framework" != "crewai" && "$crewai_integration_mode" != "compat" ]]; then
|
|
54
|
+
log_error "--integration-mode is only supported for CrewAI"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
resolve_next_steps_file() {
|
|
59
|
+
if [[ "$framework" == "crewai" ]]; then
|
|
60
|
+
if [[ "$crewai_integration_mode" == "native" ]]; then
|
|
61
|
+
echo "$FRAMEWORKS_DIR/next-steps.d/crewai-native.txt"
|
|
62
|
+
return
|
|
63
|
+
fi
|
|
64
|
+
fi
|
|
65
|
+
echo "$FRAMEWORKS_DIR/next-steps.d/$framework.txt"
|
|
66
|
+
}
|
|
20
67
|
|
|
21
68
|
run_setup() {
|
|
22
69
|
log_info "Setting up APort guardrails for $framework..."
|
|
23
70
|
config_dir="$(write_config_template "$framework")"
|
|
24
71
|
mkdir -p "$config_dir/aport"
|
|
25
72
|
chmod 700 "$config_dir/aport"
|
|
73
|
+
install_runtime_tree "$config_dir"
|
|
26
74
|
export APORT_FRAMEWORK="$framework"
|
|
27
|
-
|
|
75
|
+
if [[ "$framework" == "crewai" ]]; then
|
|
76
|
+
log_info "CrewAI integration mode: $crewai_integration_mode"
|
|
77
|
+
fi
|
|
78
|
+
if ((${#FORWARD_ARGS[@]} > 0)); then
|
|
79
|
+
run_passport_wizard "${FORWARD_ARGS[@]}"
|
|
80
|
+
else
|
|
81
|
+
run_passport_wizard
|
|
82
|
+
fi
|
|
28
83
|
# Harden permissions on passport (contains policy/capabilities)
|
|
29
84
|
[ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
|
|
85
|
+
log_info "Local runtime installed at: $config_dir/aport/runtime"
|
|
30
86
|
|
|
31
87
|
# Print framework-specific next steps from data file
|
|
32
|
-
next_steps="$
|
|
88
|
+
next_steps="$(resolve_next_steps_file)"
|
|
33
89
|
if [ -f "$next_steps" ]; then
|
|
34
90
|
echo ""
|
|
35
91
|
# Substitute $config_dir in the template
|
|
@@ -57,4 +113,4 @@ run_setup() {
|
|
|
57
113
|
fi
|
|
58
114
|
}
|
|
59
115
|
|
|
60
|
-
run_setup
|
|
116
|
+
run_setup
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Next steps (CrewAI native provider mode):
|
|
2
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
3
|
+
Requires a CrewAI build with native guardrail provider support.
|
|
4
|
+
|
|
5
|
+
1. Install the Python runtime package:
|
|
6
|
+
uv add aport-agent-guardrails
|
|
7
|
+
2. Config written to: $config_dir
|
|
8
|
+
3. Enable the native provider before running your crew:
|
|
9
|
+
|
|
10
|
+
from crewai.hooks import enable_guardrail
|
|
11
|
+
from aport_guardrails.providers import OAPGuardrailProvider
|
|
12
|
+
enable_guardrail(
|
|
13
|
+
OAPGuardrailProvider(framework="crewai", config_path="$config_dir/config.yaml"),
|
|
14
|
+
fail_closed=True,
|
|
15
|
+
)
|
|
16
|
+
crew.kickoff()
|
|
17
|
+
|
|
18
|
+
Released CrewAI compatibility mode:
|
|
19
|
+
rerun without --integration-mode=native
|
|
20
|
+
|
|
21
|
+
See: docs/frameworks/crewai.md
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
Next steps (CrewAI):
|
|
2
2
|
βββββββββββββββββββ
|
|
3
|
-
|
|
3
|
+
Default mode targets released CrewAI without native guardrail provider support.
|
|
4
|
+
|
|
5
|
+
1. Install the CrewAI adapter package:
|
|
4
6
|
pip install aport-agent-guardrails-crewai
|
|
5
7
|
aport-crewai setup
|
|
6
8
|
2. Config written to: $config_dir
|
|
@@ -10,4 +12,7 @@
|
|
|
10
12
|
register_aport_guardrail()
|
|
11
13
|
crew.kickoff()
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
Optional native mode:
|
|
16
|
+
rerun with --integration-mode=native
|
|
17
|
+
|
|
18
|
+
See: docs/frameworks/crewai.md
|
package/bin/lib/error.sh
CHANGED
|
@@ -182,13 +182,13 @@ get_resolution() {
|
|
|
182
182
|
echo "Reduce context data size by removing unnecessary fields or summarizing large data. Default limit: 100KB."
|
|
183
183
|
;;
|
|
184
184
|
"$ERROR_PATH_NOT_ALLOWED")
|
|
185
|
-
echo "Use standard
|
|
185
|
+
echo "Use standard framework config directories such as ~/.openclaw/, ~/.aport/, ~/.cursor/, ~/.claude/, ~/.n8n/, or /tmp/aport-*. Contact administrator to add custom allowed directories."
|
|
186
186
|
;;
|
|
187
187
|
"$ERROR_PATH_TRAVERSAL")
|
|
188
188
|
echo "Use absolute paths without parent directory references (../ or /..). This is a security feature to prevent path traversal attacks."
|
|
189
189
|
;;
|
|
190
190
|
"$ERROR_PASSPORT_NOT_FOUND")
|
|
191
|
-
echo "Create a passport by running: npx @aporthq/aport-agent-guardrails
|
|
191
|
+
echo "Create a passport by running: npx @aporthq/aport-agent-guardrails <framework>. See: https://github.com/aporthq/aport-agent-guardrails/tree/main/docs/frameworks"
|
|
192
192
|
;;
|
|
193
193
|
"$ERROR_PASSPORT_MISSING_CAP")
|
|
194
194
|
echo "Request capability be added to passport or generate new passport with required capabilities."
|
|
@@ -206,7 +206,7 @@ get_resolution() {
|
|
|
206
206
|
echo "Wait for rate limit to reset, reduce request frequency, or use local evaluation mode instead of API mode."
|
|
207
207
|
;;
|
|
208
208
|
"$ERROR_MISCONFIGURED")
|
|
209
|
-
echo "Run setup: npx @aporthq/aport-agent-guardrails <framework>. Check
|
|
209
|
+
echo "Run setup: npx @aporthq/aport-agent-guardrails <framework>. Check your framework config directory for aport/passport.json and aport/runtime/bin/aport-guardrail.sh."
|
|
210
210
|
;;
|
|
211
211
|
*)
|
|
212
212
|
echo ""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
file bin/aport-create-passport.sh
|
|
2
|
+
file bin/aport-guardrail.sh
|
|
3
|
+
file bin/aport-guardrail-bash.sh
|
|
4
|
+
file bin/aport-guardrail-api.sh
|
|
5
|
+
file bin/aport-guardrail-v2.sh
|
|
6
|
+
file bin/aport-resolve-paths.sh
|
|
7
|
+
file bin/aport-status.sh
|
|
8
|
+
file bin/lib/common.sh
|
|
9
|
+
file bin/lib/config.sh
|
|
10
|
+
file bin/lib/passport.sh
|
|
11
|
+
file bin/lib/tool-mapping.sh
|
|
12
|
+
file bin/lib/validation.sh
|
|
13
|
+
file python/aport_guardrails/core/tool-pack-mapping.json
|
|
14
|
+
file src/evaluator.js
|
|
15
|
+
tree external/aport-policies
|
|
16
|
+
file external/aport-spec/oap/passport-schema.json
|
|
17
|
+
mkdir local-overrides/policies
|