@aporthq/aport-agent-guardrails 1.0.8
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/LICENSE +217 -0
- package/README.md +481 -0
- package/bin/agent-guardrails +133 -0
- package/bin/aport-create-passport.sh +444 -0
- package/bin/aport-cursor-hook.sh +90 -0
- package/bin/aport-guardrail-api.sh +108 -0
- package/bin/aport-guardrail-bash.sh +394 -0
- package/bin/aport-guardrail-v2.sh +5 -0
- package/bin/aport-guardrail.sh +5 -0
- package/bin/aport-resolve-paths.sh +71 -0
- package/bin/aport-status.sh +276 -0
- package/bin/frameworks/crewai.sh +49 -0
- package/bin/frameworks/cursor.sh +95 -0
- package/bin/frameworks/langchain.sh +48 -0
- package/bin/frameworks/n8n.sh +36 -0
- package/bin/frameworks/openclaw.sh +19 -0
- package/bin/lib/allowlist.sh +18 -0
- package/bin/lib/common.sh +28 -0
- package/bin/lib/config.sh +46 -0
- package/bin/lib/constants.sh +232 -0
- package/bin/lib/detect.sh +65 -0
- package/bin/lib/error.sh +269 -0
- package/bin/lib/passport.sh +19 -0
- package/bin/lib/templates/.gitkeep +1 -0
- package/bin/lib/templates/config.yaml +6 -0
- package/bin/lib/validation.sh +206 -0
- package/bin/openclaw +660 -0
- package/docs/ADDING_A_FRAMEWORK.md +87 -0
- package/docs/AGENTS.md.example +40 -0
- package/docs/CODE_REVIEW.md +192 -0
- package/docs/DEPLOYMENT_READINESS.md +81 -0
- package/docs/FAQ_SECURITY_SCANNERS.md +373 -0
- package/docs/FRAMEWORK_ROADMAP.md +41 -0
- package/docs/HOSTED_PASSPORT_SETUP.md +362 -0
- package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +433 -0
- package/docs/OPENCLAW_COMPATIBILITY.md +73 -0
- package/docs/OPENCLAW_LOCAL_INTEGRATION.md +596 -0
- package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +54 -0
- package/docs/QUICKSTART.md +470 -0
- package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +470 -0
- package/docs/README.md +28 -0
- package/docs/RELEASE.md +87 -0
- package/docs/REPO_LAYOUT.md +47 -0
- package/docs/SKILLS_ECOSYSTEM_ANALYSIS_FEB17.md +1260 -0
- package/docs/TOOL_POLICY_MAPPING.md +46 -0
- package/docs/UPGRADE.md +46 -0
- package/docs/VERIFICATION_METHODS.md +97 -0
- package/docs/assets/README.md +8 -0
- package/docs/assets/porter.svg +54 -0
- package/docs/development/ERROR_CODES.md +616 -0
- package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +1105 -0
- package/docs/frameworks/crewai.md +114 -0
- package/docs/frameworks/cursor.md +159 -0
- package/docs/frameworks/langchain.md +72 -0
- package/docs/frameworks/n8n.md +40 -0
- package/docs/frameworks/openclaw.md +40 -0
- package/docs/launch/ADD_APORT_AWESOME_LISTS_INSTRUCTIONS.md +146 -0
- package/docs/launch/ANNOUNCEMENT_GUIDE.md +266 -0
- package/docs/launch/AWESOME_REPOS.md +53 -0
- package/docs/launch/CURSOR_VSCODE_HOOKS_RESEARCH.md +77 -0
- package/docs/launch/DEMO_TERMINAL_OUTPUT.txt +48 -0
- package/docs/launch/DRY_AND_PLAN_CHECKLIST.md +47 -0
- package/docs/launch/EVIDENCE_README.md +61 -0
- package/docs/launch/EVIDENCE_TERMINAL_CAPTURE.txt +10 -0
- package/docs/launch/FRAMEWORK_SUPPORT_PLAN.md +1640 -0
- package/docs/launch/LAUNCH_READINESS_CHECKLIST.md +237 -0
- package/docs/launch/LAUNCH_STRATEGY_SUMMARY.md +464 -0
- package/docs/launch/OPENCLAW_FEEDBACK_AND_FIXES.md +85 -0
- package/docs/launch/POST_1_VALENTINE_IMPROVED.md +233 -0
- package/docs/launch/POST_2_GUARDRAIL_IMPROVED.md +369 -0
- package/docs/launch/PRE_LAUNCH_FIXES.md +766 -0
- package/docs/launch/QUICK_LAUNCH_CHECKLIST.md +400 -0
- package/docs/launch/READINESS_SUMMARY.md +262 -0
- package/docs/launch/README.md +68 -0
- package/docs/launch/USER_STORIES.md +327 -0
- package/docs/launch/scripts/add-aport-awesome-pr.sh +69 -0
- package/docs/operations/MONITORING.md +588 -0
- package/docs/reviews/2026-02-18-staff-review.md +268 -0
- package/extensions/openclaw-aport/README.md +415 -0
- package/extensions/openclaw-aport/index.js +625 -0
- package/extensions/openclaw-aport/openclaw-aport.js +7 -0
- package/extensions/openclaw-aport/openclaw.plugin.json +46 -0
- package/extensions/openclaw-aport/package.json +36 -0
- package/extensions/openclaw-aport/test.js +307 -0
- package/external/aport-policies/README.md +363 -0
- package/external/aport-policies/agent.session.create.v1/README.md +345 -0
- package/external/aport-policies/agent.session.create.v1/policy.json +162 -0
- package/external/aport-policies/agent.tool.register.v1/README.md +361 -0
- package/external/aport-policies/agent.tool.register.v1/policy.json +172 -0
- package/external/aport-policies/code.release.publish.v1/README.md +51 -0
- package/external/aport-policies/code.release.publish.v1/policy.json +121 -0
- package/external/aport-policies/code.repository.merge.v1/README.md +287 -0
- package/external/aport-policies/code.repository.merge.v1/express.example.js +332 -0
- package/external/aport-policies/code.repository.merge.v1/fastapi.example.py +370 -0
- package/external/aport-policies/code.repository.merge.v1/policy.json +162 -0
- package/external/aport-policies/data.export.create.v1/README.md +226 -0
- package/external/aport-policies/data.export.create.v1/express.example.js +172 -0
- package/external/aport-policies/data.export.create.v1/fastapi.example.py +165 -0
- package/external/aport-policies/data.export.create.v1/policy.json +133 -0
- package/external/aport-policies/data.report.ingest.v1/README.md +134 -0
- package/external/aport-policies/data.report.ingest.v1/express.example.js +105 -0
- package/external/aport-policies/data.report.ingest.v1/minimal-example.js +68 -0
- package/external/aport-policies/data.report.ingest.v1/policy.json +174 -0
- package/external/aport-policies/finance.crypto.trade.v1/README.md +146 -0
- package/external/aport-policies/finance.crypto.trade.v1/express.example.js +109 -0
- package/external/aport-policies/finance.crypto.trade.v1/minimal-example.js +65 -0
- package/external/aport-policies/finance.crypto.trade.v1/policy.json +176 -0
- package/external/aport-policies/finance.payment.charge.v1/README.md +326 -0
- package/external/aport-policies/finance.payment.charge.v1/express.example.js +250 -0
- package/external/aport-policies/finance.payment.charge.v1/fastapi.example.py +227 -0
- package/external/aport-policies/finance.payment.charge.v1/minimal-example.js +64 -0
- package/external/aport-policies/finance.payment.charge.v1/policy.json +224 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/passport.template.json +40 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/payments-charge-policy.test.js +817 -0
- package/external/aport-policies/finance.payment.charge.v1/tests/test_payments_charge_policy.py +486 -0
- package/external/aport-policies/finance.payment.payout.v1/README.md +78 -0
- package/external/aport-policies/finance.payment.payout.v1/policy.json +181 -0
- package/external/aport-policies/finance.payment.refund.v1/README.md +275 -0
- package/external/aport-policies/finance.payment.refund.v1/express.example.js +167 -0
- package/external/aport-policies/finance.payment.refund.v1/fastapi.example.py +136 -0
- package/external/aport-policies/finance.payment.refund.v1/minimal-example.js +183 -0
- package/external/aport-policies/finance.payment.refund.v1/policy.json +216 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/refunds-policy.test.js +924 -0
- package/external/aport-policies/finance.payment.refund.v1/tests/test_refunds_policy.py +778 -0
- package/external/aport-policies/finance.transaction.execute.v1/README.md +309 -0
- package/external/aport-policies/finance.transaction.execute.v1/express.example.js +261 -0
- package/external/aport-policies/finance.transaction.execute.v1/fastapi.example.py +231 -0
- package/external/aport-policies/finance.transaction.execute.v1/minimal-example.js +78 -0
- package/external/aport-policies/finance.transaction.execute.v1/policy.json +189 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.instance.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/passport.template.json +42 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py +214 -0
- package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js +306 -0
- package/external/aport-policies/governance.data.access.v1/README.md +292 -0
- package/external/aport-policies/governance.data.access.v1/express.example.js +321 -0
- package/external/aport-policies/governance.data.access.v1/fastapi.example.py +279 -0
- package/external/aport-policies/governance.data.access.v1/minimal-example.js +65 -0
- package/external/aport-policies/governance.data.access.v1/policy.json +208 -0
- package/external/aport-policies/governance.data.access.v1/tests/contexts.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/data-access-policy.test.js +308 -0
- package/external/aport-policies/governance.data.access.v1/tests/expected.jsonl +12 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.instance.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/passport.template.json +56 -0
- package/external/aport-policies/governance.data.access.v1/tests/test_data_access_policy.py +214 -0
- package/external/aport-policies/legal.contract.review.v1/README.md +109 -0
- package/external/aport-policies/legal.contract.review.v1/policy.json +378 -0
- package/external/aport-policies/legal.contract.review.v1/tests/legal-contract-review-policy.test.js +609 -0
- package/external/aport-policies/legal.contract.review.v1/tests/passport.template.json +49 -0
- package/external/aport-policies/mcp.tool.execute.v1/README.md +301 -0
- package/external/aport-policies/mcp.tool.execute.v1/policy.json +141 -0
- package/external/aport-policies/messaging.message.send.v1/README.md +230 -0
- package/external/aport-policies/messaging.message.send.v1/express.example.js +183 -0
- package/external/aport-policies/messaging.message.send.v1/fastapi.example.py +193 -0
- package/external/aport-policies/messaging.message.send.v1/policy.json +144 -0
- package/external/aport-policies/policy-template.json +107 -0
- package/external/aport-policies/system.command.execute.v1/README.md +275 -0
- package/external/aport-policies/system.command.execute.v1/policy.json +146 -0
- package/external/aport-spec/CONTRIBUTING.md +273 -0
- package/external/aport-spec/LICENSE +21 -0
- package/external/aport-spec/README.md +168 -0
- package/external/aport-spec/conformance/README.md +294 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/allow_users.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/contexts/deny_pii.json +6 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/allow_users.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/expected/deny_pii.decision.json +19 -0
- package/external/aport-spec/conformance/cases/data.export.v1/passports/template.json +29 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/allow_50usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_150usd.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_currency.json +9 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/allow_50usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_150usd.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_currency.decision.json +19 -0
- package/external/aport-spec/conformance/cases/payments.refunds.v1/passports/template.json +42 -0
- package/external/aport-spec/conformance/package.json +44 -0
- package/external/aport-spec/conformance/pnpm-lock.yaml +642 -0
- package/external/aport-spec/conformance/src/cases.ts +371 -0
- package/external/aport-spec/conformance/src/ed25519.ts +167 -0
- package/external/aport-spec/conformance/src/jcs.ts +85 -0
- package/external/aport-spec/conformance/src/runner.ts +533 -0
- package/external/aport-spec/conformance/src/validators.ts +185 -0
- package/external/aport-spec/conformance/test-runner.js +315 -0
- package/external/aport-spec/conformance/tsconfig.json +21 -0
- package/external/aport-spec/error-schema.json +192 -0
- package/external/aport-spec/index.json +12 -0
- package/external/aport-spec/integrations/clawmoat/README.md +12 -0
- package/external/aport-spec/integrations/shield/README.md +245 -0
- package/external/aport-spec/integrations/shield/adapters/index.js +116 -0
- package/external/aport-spec/integrations/shield/adapters/system-command-execute.js +133 -0
- package/external/aport-spec/integrations/shield/test/README.md +58 -0
- package/external/aport-spec/integrations/shield/test/shield.md +40 -0
- package/external/aport-spec/integrations/shield/test/test-shield-to-verify.js +274 -0
- package/external/aport-spec/metrics-schema.json +504 -0
- package/external/aport-spec/oap/CHANGELOG.md +54 -0
- package/external/aport-spec/oap/VERSION.md +40 -0
- package/external/aport-spec/oap/capability-registry.md +229 -0
- package/external/aport-spec/oap/conformance.md +257 -0
- package/external/aport-spec/oap/decision-schema.json +114 -0
- package/external/aport-spec/oap/examples/context.refund.usd.50.json +9 -0
- package/external/aport-spec/oap/examples/decision.allow.sample.json +20 -0
- package/external/aport-spec/oap/examples/decision.deny.sample.json +23 -0
- package/external/aport-spec/oap/examples/passport.instance.v1.json +50 -0
- package/external/aport-spec/oap/examples/passport.template.v1.json +71 -0
- package/external/aport-spec/oap/oap-spec.md +426 -0
- package/external/aport-spec/oap/passport-schema.json +396 -0
- package/external/aport-spec/oap/security.md +213 -0
- package/external/aport-spec/oap/vc/context-oap-v1.jsonld +137 -0
- package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +37 -0
- package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +68 -0
- package/external/aport-spec/oap/vc/tools/INTEGRATION.md +375 -0
- package/external/aport-spec/oap/vc/tools/README.md +278 -0
- package/external/aport-spec/oap/vc/tools/examples/decision-to-vc.js +66 -0
- package/external/aport-spec/oap/vc/tools/examples/passport-to-vc.js +83 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +77 -0
- package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +94 -0
- package/external/aport-spec/oap/vc/tools/package.json +38 -0
- package/external/aport-spec/oap/vc/tools/pnpm-lock.yaml +472 -0
- package/external/aport-spec/oap/vc/tools/src/cli.ts +226 -0
- package/external/aport-spec/oap/vc/tools/src/crypto-utils.ts +427 -0
- package/external/aport-spec/oap/vc/tools/src/index.ts +653 -0
- package/external/aport-spec/oap/vc/tools/src/test.ts +148 -0
- package/external/aport-spec/oap/vc/tools/src/vp.ts +382 -0
- package/external/aport-spec/oap/vc/tools/test-simple.js +214 -0
- package/external/aport-spec/oap/vc/tools/tsconfig.json +19 -0
- package/external/aport-spec/oap/vc/vc-mapping.md +443 -0
- package/external/aport-spec/passport-schema.json +586 -0
- package/external/aport-spec/rate-limiting.md +136 -0
- package/external/aport-spec/transport-profile.md +325 -0
- package/external/aport-spec/webhook-spec.md +314 -0
- package/package.json +70 -0
- package/skills/aport-agent-guardrail/SKILL.md +314 -0
- package/src/evaluator.js +252 -0
- package/src/server/index.js +72 -0
package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test Suite: finance.transaction.execute.v1 Policy
|
|
3
|
+
|
|
4
|
+
Tests the financial transaction execution policy with various scenarios
|
|
5
|
+
including valid transactions, limit violations, and security controls.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from typing import Dict, Any, List
|
|
11
|
+
|
|
12
|
+
def evaluate_finance_transaction_execute_v1(passport: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
|
|
13
|
+
"""Mock implementation of the finance.transaction.execute.v1 policy evaluation"""
|
|
14
|
+
reasons = []
|
|
15
|
+
allow = True
|
|
16
|
+
|
|
17
|
+
# Check agent status
|
|
18
|
+
if passport.get("status") in ["suspended", "revoked"]:
|
|
19
|
+
return {
|
|
20
|
+
"allow": False,
|
|
21
|
+
"reasons": [{
|
|
22
|
+
"code": "oap.passport_suspended",
|
|
23
|
+
"message": f"Agent is {passport.get('status')} and cannot perform operations",
|
|
24
|
+
"severity": "error"
|
|
25
|
+
}]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Check capabilities
|
|
29
|
+
capabilities = passport.get("capabilities", [])
|
|
30
|
+
has_transaction_capability = any(
|
|
31
|
+
cap.get("id") == "finance.transaction" for cap in capabilities
|
|
32
|
+
)
|
|
33
|
+
if not has_transaction_capability:
|
|
34
|
+
return {
|
|
35
|
+
"allow": False,
|
|
36
|
+
"reasons": [{
|
|
37
|
+
"code": "oap.unknown_capability",
|
|
38
|
+
"message": "Agent does not have finance.transaction capability",
|
|
39
|
+
"severity": "error"
|
|
40
|
+
}]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Check assurance level
|
|
44
|
+
required_assurance = passport.get("limits", {}).get("finance", {}).get("transaction", {}).get("require_assurance_at_least", "L3")
|
|
45
|
+
assurance_level = passport.get("assurance_level")
|
|
46
|
+
if assurance_level not in [required_assurance, "L4KYC", "L4FIN"]:
|
|
47
|
+
return {
|
|
48
|
+
"allow": False,
|
|
49
|
+
"reasons": [{
|
|
50
|
+
"code": "oap.assurance_insufficient",
|
|
51
|
+
"message": f"Assurance level {assurance_level} is insufficient, requires {required_assurance}",
|
|
52
|
+
"severity": "error"
|
|
53
|
+
}]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Check required fields
|
|
57
|
+
required_fields = ["transaction_type", "amount", "currency", "asset_class", "source_account_id", "destination_account_id"]
|
|
58
|
+
missing_fields = [field for field in required_fields if not context.get(field)]
|
|
59
|
+
if missing_fields:
|
|
60
|
+
return {
|
|
61
|
+
"allow": False,
|
|
62
|
+
"reasons": [{
|
|
63
|
+
"code": "oap.invalid_context",
|
|
64
|
+
"message": f"Missing required fields: {', '.join(missing_fields)}",
|
|
65
|
+
"severity": "error"
|
|
66
|
+
}]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Check transaction type is allowed
|
|
70
|
+
allowed_transaction_types = passport.get("limits", {}).get("finance", {}).get("transaction", {}).get("allowed_transaction_types", [])
|
|
71
|
+
if allowed_transaction_types and context.get("transaction_type") not in allowed_transaction_types:
|
|
72
|
+
return {
|
|
73
|
+
"allow": False,
|
|
74
|
+
"reasons": [{
|
|
75
|
+
"code": "oap.action_forbidden",
|
|
76
|
+
"message": f"Transaction type {context.get('transaction_type')} is not allowed",
|
|
77
|
+
"severity": "error"
|
|
78
|
+
}]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Check asset class is allowed
|
|
82
|
+
allowed_asset_classes = passport.get("limits", {}).get("finance", {}).get("transaction", {}).get("allowed_asset_classes", [])
|
|
83
|
+
if allowed_asset_classes and context.get("asset_class") not in allowed_asset_classes:
|
|
84
|
+
return {
|
|
85
|
+
"allow": False,
|
|
86
|
+
"reasons": [{
|
|
87
|
+
"code": "oap.asset_class_forbidden",
|
|
88
|
+
"message": f"Asset class {context.get('asset_class')} is not allowed",
|
|
89
|
+
"severity": "error"
|
|
90
|
+
}]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Check exposure limit
|
|
94
|
+
max_exposure = passport.get("limits", {}).get("finance", {}).get("transaction", {}).get("max_exposure_per_tx_usd")
|
|
95
|
+
if max_exposure and context.get("amount", 0) > max_exposure:
|
|
96
|
+
return {
|
|
97
|
+
"allow": False,
|
|
98
|
+
"reasons": [{
|
|
99
|
+
"code": "oap.limit_exceeded",
|
|
100
|
+
"message": f"Amount {context.get('amount')} exceeds maximum exposure limit {max_exposure}",
|
|
101
|
+
"severity": "error"
|
|
102
|
+
}]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Check source account type restrictions
|
|
106
|
+
restricted_account_types = passport.get("limits", {}).get("finance", {}).get("transaction", {}).get("restricted_source_account_types", [])
|
|
107
|
+
if context.get("source_account_type") in restricted_account_types:
|
|
108
|
+
return {
|
|
109
|
+
"allow": False,
|
|
110
|
+
"reasons": [{
|
|
111
|
+
"code": "oap.account_type_restricted",
|
|
112
|
+
"message": f"Source account type {context.get('source_account_type')} is restricted",
|
|
113
|
+
"severity": "error"
|
|
114
|
+
}]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Check allowed source account types
|
|
118
|
+
allowed_source_account_types = passport.get("limits", {}).get("finance", {}).get("transaction", {}).get("allowed_source_account_types", [])
|
|
119
|
+
if allowed_source_account_types and context.get("source_account_type") and context.get("source_account_type") not in allowed_source_account_types:
|
|
120
|
+
return {
|
|
121
|
+
"allow": False,
|
|
122
|
+
"reasons": [{
|
|
123
|
+
"code": "oap.account_type_restricted",
|
|
124
|
+
"message": f"Source account type {context.get('source_account_type')} is not allowed",
|
|
125
|
+
"severity": "error"
|
|
126
|
+
}]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Check segregation of funds (prevent commingling)
|
|
130
|
+
if context.get("source_account_type") == "client_funds" and context.get("destination_account_type") == "proprietary":
|
|
131
|
+
return {
|
|
132
|
+
"allow": False,
|
|
133
|
+
"reasons": [{
|
|
134
|
+
"code": "oap.commingling_of_funds_forbidden",
|
|
135
|
+
"message": "Cannot transfer from client funds to proprietary accounts",
|
|
136
|
+
"severity": "error"
|
|
137
|
+
}]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Check counterparty exposure limit
|
|
141
|
+
max_counterparty_exposure = passport.get("limits", {}).get("finance", {}).get("transaction", {}).get("max_exposure_per_counterparty_usd")
|
|
142
|
+
if max_counterparty_exposure and context.get("counterparty_id") and context.get("amount", 0) > max_counterparty_exposure:
|
|
143
|
+
return {
|
|
144
|
+
"allow": False,
|
|
145
|
+
"reasons": [{
|
|
146
|
+
"code": "oap.counterparty_limit_exceeded",
|
|
147
|
+
"message": f"Amount {context.get('amount')} exceeds counterparty exposure limit {max_counterparty_exposure}",
|
|
148
|
+
"severity": "error"
|
|
149
|
+
}]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# If all checks pass, allow the transaction
|
|
153
|
+
return {
|
|
154
|
+
"allow": True,
|
|
155
|
+
"reasons": [{
|
|
156
|
+
"code": "oap.allowed",
|
|
157
|
+
"message": "Transaction within limits and policy requirements",
|
|
158
|
+
"severity": "info"
|
|
159
|
+
}]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
def run_tests():
|
|
163
|
+
"""Run the test suite"""
|
|
164
|
+
print("๐งช Running finance.transaction.execute.v1 Policy Tests\n")
|
|
165
|
+
|
|
166
|
+
# Load test data
|
|
167
|
+
test_dir = os.path.dirname(os.path.abspath(__file__))
|
|
168
|
+
|
|
169
|
+
with open(os.path.join(test_dir, "passport.instance.json"), "r") as f:
|
|
170
|
+
passport = json.load(f)
|
|
171
|
+
|
|
172
|
+
with open(os.path.join(test_dir, "contexts.jsonl"), "r") as f:
|
|
173
|
+
contexts = [json.loads(line) for line in f.read().strip().split("\n")]
|
|
174
|
+
|
|
175
|
+
with open(os.path.join(test_dir, "expected.jsonl"), "r") as f:
|
|
176
|
+
expected = [json.loads(line) for line in f.read().strip().split("\n")]
|
|
177
|
+
|
|
178
|
+
passed = 0
|
|
179
|
+
failed = 0
|
|
180
|
+
|
|
181
|
+
for i, test_case in enumerate(contexts):
|
|
182
|
+
expected_result = expected[i]
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
result = evaluate_finance_transaction_execute_v1(passport, test_case["context"])
|
|
186
|
+
|
|
187
|
+
# Compare results
|
|
188
|
+
allow_match = result["allow"] == expected_result["expected"]["allow"]
|
|
189
|
+
reasons_match = json.dumps(result["reasons"]) == json.dumps(expected_result["expected"]["reasons"])
|
|
190
|
+
|
|
191
|
+
if allow_match and reasons_match:
|
|
192
|
+
print(f"โ
{test_case['name']}: PASS")
|
|
193
|
+
passed += 1
|
|
194
|
+
else:
|
|
195
|
+
print(f"โ {test_case['name']}: FAIL")
|
|
196
|
+
print(f" Expected: {json.dumps(expected_result['expected'])}")
|
|
197
|
+
print(f" Got: {json.dumps(result)}")
|
|
198
|
+
failed += 1
|
|
199
|
+
except Exception as error:
|
|
200
|
+
print(f"โ {test_case['name']}: ERROR - {str(error)}")
|
|
201
|
+
failed += 1
|
|
202
|
+
|
|
203
|
+
print(f"\n๐ Test Results: {passed} passed, {failed} failed")
|
|
204
|
+
|
|
205
|
+
if failed == 0:
|
|
206
|
+
print("๐ All tests passed!")
|
|
207
|
+
return True
|
|
208
|
+
else:
|
|
209
|
+
print("๐ฅ Some tests failed!")
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
if __name__ == "__main__":
|
|
213
|
+
success = run_tests()
|
|
214
|
+
exit(0 if success else 1)
|
package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Suite: finance.transaction.execute.v1 Policy
|
|
3
|
+
*
|
|
4
|
+
* Tests the financial transaction execution policy with various scenarios
|
|
5
|
+
* including valid transactions, limit violations, and security controls.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
// Mock the policy evaluation function
|
|
12
|
+
async function evaluateFinanceTransactionExecuteV1(passport, context) {
|
|
13
|
+
// Mock implementation based on the actual policy logic
|
|
14
|
+
const reasons = [];
|
|
15
|
+
let allow = true;
|
|
16
|
+
|
|
17
|
+
// Check agent status
|
|
18
|
+
if (passport.status === "suspended" || passport.status === "revoked") {
|
|
19
|
+
return {
|
|
20
|
+
allow: false,
|
|
21
|
+
reasons: [
|
|
22
|
+
{
|
|
23
|
+
code: "oap.passport_suspended",
|
|
24
|
+
message: `Agent is ${passport.status} and cannot perform operations`,
|
|
25
|
+
severity: "error",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check capabilities
|
|
32
|
+
const hasTransactionCapability = passport.capabilities?.some(
|
|
33
|
+
(cap) => cap.id === "finance.transaction"
|
|
34
|
+
);
|
|
35
|
+
if (!hasTransactionCapability) {
|
|
36
|
+
return {
|
|
37
|
+
allow: false,
|
|
38
|
+
reasons: [
|
|
39
|
+
{
|
|
40
|
+
code: "oap.unknown_capability",
|
|
41
|
+
message: "Agent does not have finance.transaction capability",
|
|
42
|
+
severity: "error",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check assurance level
|
|
49
|
+
const requiredAssurance =
|
|
50
|
+
passport.limits?.finance?.transaction?.require_assurance_at_least || "L3";
|
|
51
|
+
if (
|
|
52
|
+
passport.assurance_level !== requiredAssurance &&
|
|
53
|
+
passport.assurance_level !== "L4KYC" &&
|
|
54
|
+
passport.assurance_level !== "L4FIN"
|
|
55
|
+
) {
|
|
56
|
+
return {
|
|
57
|
+
allow: false,
|
|
58
|
+
reasons: [
|
|
59
|
+
{
|
|
60
|
+
code: "oap.assurance_insufficient",
|
|
61
|
+
message: `Assurance level ${passport.assurance_level} is insufficient, requires ${requiredAssurance}`,
|
|
62
|
+
severity: "error",
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check required fields
|
|
69
|
+
const requiredFields = [
|
|
70
|
+
"transaction_type",
|
|
71
|
+
"amount",
|
|
72
|
+
"currency",
|
|
73
|
+
"asset_class",
|
|
74
|
+
"source_account_id",
|
|
75
|
+
"destination_account_id",
|
|
76
|
+
];
|
|
77
|
+
const missingFields = requiredFields.filter((field) => !context[field]);
|
|
78
|
+
if (missingFields.length > 0) {
|
|
79
|
+
return {
|
|
80
|
+
allow: false,
|
|
81
|
+
reasons: [
|
|
82
|
+
{
|
|
83
|
+
code: "oap.invalid_context",
|
|
84
|
+
message: `Missing required fields: ${missingFields.join(", ")}`,
|
|
85
|
+
severity: "error",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check transaction type is allowed
|
|
92
|
+
const allowedTransactionTypes =
|
|
93
|
+
passport.limits?.finance?.transaction?.allowed_transaction_types || [];
|
|
94
|
+
if (
|
|
95
|
+
allowedTransactionTypes.length > 0 &&
|
|
96
|
+
!allowedTransactionTypes.includes(context.transaction_type)
|
|
97
|
+
) {
|
|
98
|
+
return {
|
|
99
|
+
allow: false,
|
|
100
|
+
reasons: [
|
|
101
|
+
{
|
|
102
|
+
code: "oap.action_forbidden",
|
|
103
|
+
message: `Transaction type ${context.transaction_type} is not allowed`,
|
|
104
|
+
severity: "error",
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check asset class is allowed
|
|
111
|
+
const allowedAssetClasses =
|
|
112
|
+
passport.limits?.finance?.transaction?.allowed_asset_classes || [];
|
|
113
|
+
if (
|
|
114
|
+
allowedAssetClasses.length > 0 &&
|
|
115
|
+
!allowedAssetClasses.includes(context.asset_class)
|
|
116
|
+
) {
|
|
117
|
+
return {
|
|
118
|
+
allow: false,
|
|
119
|
+
reasons: [
|
|
120
|
+
{
|
|
121
|
+
code: "oap.asset_class_forbidden",
|
|
122
|
+
message: `Asset class ${context.asset_class} is not allowed`,
|
|
123
|
+
severity: "error",
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check exposure limit
|
|
130
|
+
const maxExposure =
|
|
131
|
+
passport.limits?.finance?.transaction?.max_exposure_per_tx_usd;
|
|
132
|
+
if (maxExposure && context.amount > maxExposure) {
|
|
133
|
+
return {
|
|
134
|
+
allow: false,
|
|
135
|
+
reasons: [
|
|
136
|
+
{
|
|
137
|
+
code: "oap.limit_exceeded",
|
|
138
|
+
message: `Amount ${context.amount} exceeds maximum exposure limit ${maxExposure}`,
|
|
139
|
+
severity: "error",
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check source account type restrictions
|
|
146
|
+
const restrictedAccountTypes =
|
|
147
|
+
passport.limits?.finance?.transaction?.restricted_source_account_types ||
|
|
148
|
+
[];
|
|
149
|
+
if (
|
|
150
|
+
context.source_account_type &&
|
|
151
|
+
restrictedAccountTypes.includes(context.source_account_type)
|
|
152
|
+
) {
|
|
153
|
+
return {
|
|
154
|
+
allow: false,
|
|
155
|
+
reasons: [
|
|
156
|
+
{
|
|
157
|
+
code: "oap.account_type_restricted",
|
|
158
|
+
message: `Source account type ${context.source_account_type} is restricted`,
|
|
159
|
+
severity: "error",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check allowed source account types
|
|
166
|
+
const allowedSourceAccountTypes =
|
|
167
|
+
passport.limits?.finance?.transaction?.allowed_source_account_types || [];
|
|
168
|
+
if (
|
|
169
|
+
allowedSourceAccountTypes.length > 0 &&
|
|
170
|
+
context.source_account_type &&
|
|
171
|
+
!allowedSourceAccountTypes.includes(context.source_account_type)
|
|
172
|
+
) {
|
|
173
|
+
return {
|
|
174
|
+
allow: false,
|
|
175
|
+
reasons: [
|
|
176
|
+
{
|
|
177
|
+
code: "oap.account_type_restricted",
|
|
178
|
+
message: `Source account type ${context.source_account_type} is not allowed`,
|
|
179
|
+
severity: "error",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check segregation of funds (prevent commingling)
|
|
186
|
+
if (
|
|
187
|
+
context.source_account_type === "client_funds" &&
|
|
188
|
+
context.destination_account_type === "proprietary"
|
|
189
|
+
) {
|
|
190
|
+
return {
|
|
191
|
+
allow: false,
|
|
192
|
+
reasons: [
|
|
193
|
+
{
|
|
194
|
+
code: "oap.commingling_of_funds_forbidden",
|
|
195
|
+
message: "Cannot transfer from client funds to proprietary accounts",
|
|
196
|
+
severity: "error",
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Check counterparty exposure limit
|
|
203
|
+
const maxCounterpartyExposure =
|
|
204
|
+
passport.limits?.finance?.transaction?.max_exposure_per_counterparty_usd;
|
|
205
|
+
if (
|
|
206
|
+
maxCounterpartyExposure &&
|
|
207
|
+
context.counterparty_id &&
|
|
208
|
+
context.amount > maxCounterpartyExposure
|
|
209
|
+
) {
|
|
210
|
+
return {
|
|
211
|
+
allow: false,
|
|
212
|
+
reasons: [
|
|
213
|
+
{
|
|
214
|
+
code: "oap.counterparty_limit_exceeded",
|
|
215
|
+
message: `Amount ${context.amount} exceeds counterparty exposure limit ${maxCounterpartyExposure}`,
|
|
216
|
+
severity: "error",
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// If all checks pass, allow the transaction
|
|
223
|
+
return {
|
|
224
|
+
allow: true,
|
|
225
|
+
reasons: [
|
|
226
|
+
{
|
|
227
|
+
code: "oap.allowed",
|
|
228
|
+
message: "Transaction within limits and policy requirements",
|
|
229
|
+
severity: "info",
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Test runner
|
|
236
|
+
async function runTests() {
|
|
237
|
+
console.log("๐งช Running finance.transaction.execute.v1 Policy Tests\n");
|
|
238
|
+
|
|
239
|
+
// Load test data
|
|
240
|
+
const passportPath = path.join(__dirname, "passport.instance.json");
|
|
241
|
+
const contextsPath = path.join(__dirname, "contexts.jsonl");
|
|
242
|
+
const expectedPath = path.join(__dirname, "expected.jsonl");
|
|
243
|
+
|
|
244
|
+
const passport = JSON.parse(fs.readFileSync(passportPath, "utf8"));
|
|
245
|
+
const contexts = fs
|
|
246
|
+
.readFileSync(contextsPath, "utf8")
|
|
247
|
+
.trim()
|
|
248
|
+
.split("\n")
|
|
249
|
+
.map((line) => JSON.parse(line));
|
|
250
|
+
const expected = fs
|
|
251
|
+
.readFileSync(expectedPath, "utf8")
|
|
252
|
+
.trim()
|
|
253
|
+
.split("\n")
|
|
254
|
+
.map((line) => JSON.parse(line));
|
|
255
|
+
|
|
256
|
+
let passed = 0;
|
|
257
|
+
let failed = 0;
|
|
258
|
+
|
|
259
|
+
for (let i = 0; i < contexts.length; i++) {
|
|
260
|
+
const testCase = contexts[i];
|
|
261
|
+
const expectedResult = expected[i];
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const result = await evaluateFinanceTransactionExecuteV1(
|
|
265
|
+
passport,
|
|
266
|
+
testCase.context
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Compare results
|
|
270
|
+
const allowMatch = result.allow === expectedResult.expected.allow;
|
|
271
|
+
const reasonsMatch =
|
|
272
|
+
JSON.stringify(result.reasons) ===
|
|
273
|
+
JSON.stringify(expectedResult.expected.reasons);
|
|
274
|
+
|
|
275
|
+
if (allowMatch && reasonsMatch) {
|
|
276
|
+
console.log(`โ
${testCase.name}: PASS`);
|
|
277
|
+
passed++;
|
|
278
|
+
} else {
|
|
279
|
+
console.log(`โ ${testCase.name}: FAIL`);
|
|
280
|
+
console.log(` Expected: ${JSON.stringify(expectedResult.expected)}`);
|
|
281
|
+
console.log(` Got: ${JSON.stringify(result)}`);
|
|
282
|
+
failed++;
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.log(`โ ${testCase.name}: ERROR - ${error.message}`);
|
|
286
|
+
failed++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
console.log(`\n๐ Test Results: ${passed} passed, ${failed} failed`);
|
|
291
|
+
|
|
292
|
+
if (failed === 0) {
|
|
293
|
+
console.log("๐ All tests passed!");
|
|
294
|
+
process.exit(0);
|
|
295
|
+
} else {
|
|
296
|
+
console.log("๐ฅ Some tests failed!");
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Run tests if this file is executed directly
|
|
302
|
+
if (require.main === module) {
|
|
303
|
+
runTests().catch(console.error);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
module.exports = { evaluateFinanceTransactionExecuteV1, runTests };
|