@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/legal.contract.review.v1/tests/legal-contract-review-policy.test.js
ADDED
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Suite: legal.contract.review.v1 Policy
|
|
3
|
+
*
|
|
4
|
+
* Tests the legal contract review policy with various scenarios
|
|
5
|
+
* including valid reviews, document type violations, jurisdiction controls,
|
|
6
|
+
* attorney review requirements, and privilege protection.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
test,
|
|
11
|
+
expect,
|
|
12
|
+
describe,
|
|
13
|
+
beforeEach,
|
|
14
|
+
afterEach,
|
|
15
|
+
} = require("@jest/globals");
|
|
16
|
+
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
|
|
20
|
+
// Load passport template
|
|
21
|
+
const passportTemplate = JSON.parse(
|
|
22
|
+
fs.readFileSync(
|
|
23
|
+
path.join(__dirname, "passport.template.json"),
|
|
24
|
+
"utf8"
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Mock the policy evaluation function
|
|
29
|
+
async function evaluateLegalContractReviewV1(passport, context) {
|
|
30
|
+
const reasons = [];
|
|
31
|
+
let allow = true;
|
|
32
|
+
|
|
33
|
+
// Check agent status
|
|
34
|
+
if (passport.status === "suspended" || passport.status === "revoked") {
|
|
35
|
+
return {
|
|
36
|
+
allow: false,
|
|
37
|
+
reasons: [
|
|
38
|
+
{
|
|
39
|
+
code: "oap.passport_suspended",
|
|
40
|
+
message: `Agent is ${passport.status} and cannot perform operations`,
|
|
41
|
+
severity: "error",
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check capabilities
|
|
48
|
+
const hasLegalContractReviewCapability = passport.capabilities?.some(
|
|
49
|
+
(cap) => cap.id === "legal.contract.review"
|
|
50
|
+
);
|
|
51
|
+
if (!hasLegalContractReviewCapability) {
|
|
52
|
+
return {
|
|
53
|
+
allow: false,
|
|
54
|
+
reasons: [
|
|
55
|
+
{
|
|
56
|
+
code: "oap.unknown_capability",
|
|
57
|
+
message: "Agent does not have legal.contract.review capability",
|
|
58
|
+
severity: "error",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check assurance level
|
|
65
|
+
const requiredAssurance =
|
|
66
|
+
passport.limits?.legal?.contract?.review?.require_assurance_at_least ||
|
|
67
|
+
"L3";
|
|
68
|
+
const assuranceLevels = ["L0", "L1", "L2", "L3", "L4KYC", "L4FIN"];
|
|
69
|
+
const currentLevel = assuranceLevels.indexOf(passport.assurance_level);
|
|
70
|
+
const requiredLevel = assuranceLevels.indexOf(requiredAssurance);
|
|
71
|
+
if (currentLevel < requiredLevel) {
|
|
72
|
+
return {
|
|
73
|
+
allow: false,
|
|
74
|
+
reasons: [
|
|
75
|
+
{
|
|
76
|
+
code: "oap.assurance_insufficient",
|
|
77
|
+
message: `Assurance level ${passport.assurance_level} is insufficient, requires ${requiredAssurance}`,
|
|
78
|
+
severity: "error",
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check required fields
|
|
85
|
+
const requiredFields = [
|
|
86
|
+
"document_type",
|
|
87
|
+
"client_id",
|
|
88
|
+
"jurisdiction",
|
|
89
|
+
"action_type",
|
|
90
|
+
"idempotency_key",
|
|
91
|
+
];
|
|
92
|
+
const missingFields = requiredFields.filter((field) => !context[field]);
|
|
93
|
+
if (missingFields.length > 0) {
|
|
94
|
+
return {
|
|
95
|
+
allow: false,
|
|
96
|
+
reasons: [
|
|
97
|
+
{
|
|
98
|
+
code: "oap.invalid_context",
|
|
99
|
+
message: `Missing required fields: ${missingFields.join(", ")}`,
|
|
100
|
+
severity: "error",
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check document type is allowed
|
|
107
|
+
const allowedDocumentTypes =
|
|
108
|
+
passport.limits?.legal?.contract?.review?.allowed_document_types || [];
|
|
109
|
+
if (
|
|
110
|
+
allowedDocumentTypes.length > 0 &&
|
|
111
|
+
!allowedDocumentTypes.includes(context.document_type)
|
|
112
|
+
) {
|
|
113
|
+
return {
|
|
114
|
+
allow: false,
|
|
115
|
+
reasons: [
|
|
116
|
+
{
|
|
117
|
+
code: "oap.document_type_forbidden",
|
|
118
|
+
message: `Document type ${context.document_type} is not allowed`,
|
|
119
|
+
severity: "error",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Check document size limit
|
|
126
|
+
const maxDocumentSizeMb =
|
|
127
|
+
passport.limits?.legal?.contract?.review?.max_document_size_mb;
|
|
128
|
+
if (
|
|
129
|
+
context.document_size_mb &&
|
|
130
|
+
maxDocumentSizeMb &&
|
|
131
|
+
context.document_size_mb > maxDocumentSizeMb
|
|
132
|
+
) {
|
|
133
|
+
return {
|
|
134
|
+
allow: false,
|
|
135
|
+
reasons: [
|
|
136
|
+
{
|
|
137
|
+
code: "oap.document_size_exceeded",
|
|
138
|
+
message: `Document size ${context.document_size_mb}MB exceeds maximum allowed ${maxDocumentSizeMb}MB`,
|
|
139
|
+
severity: "error",
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check jurisdiction is authorized
|
|
146
|
+
const allowedJurisdictions =
|
|
147
|
+
passport.limits?.legal?.contract?.review?.allowed_contract_jurisdictions ||
|
|
148
|
+
[];
|
|
149
|
+
if (
|
|
150
|
+
allowedJurisdictions.length > 0 &&
|
|
151
|
+
!allowedJurisdictions.includes(context.jurisdiction)
|
|
152
|
+
) {
|
|
153
|
+
return {
|
|
154
|
+
allow: false,
|
|
155
|
+
reasons: [
|
|
156
|
+
{
|
|
157
|
+
code: "oap.jurisdiction_blocked",
|
|
158
|
+
message: `Jurisdiction ${context.jurisdiction} is not authorized for contract review`,
|
|
159
|
+
severity: "error",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check high-value contract review requirement FIRST (over $10,000 requires attorney review)
|
|
166
|
+
// This check takes precedence to provide specific error code for high-value contracts
|
|
167
|
+
const contractValueUsd = context.contract_value_usd;
|
|
168
|
+
if (contractValueUsd && contractValueUsd >= 1000000 && !context.attorney_reviewer_id) {
|
|
169
|
+
// 1000000 = $10,000 in minor units (cents)
|
|
170
|
+
return {
|
|
171
|
+
allow: false,
|
|
172
|
+
reasons: [
|
|
173
|
+
{
|
|
174
|
+
code: "oap.high_value_review_required",
|
|
175
|
+
message: "High-value contracts (over $10,000) require attorney review",
|
|
176
|
+
severity: "error",
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check general attorney review requirement (for all contracts when configured)
|
|
183
|
+
const requireAttorneyReview =
|
|
184
|
+
passport.limits?.legal?.contract?.review?.require_attorney_review;
|
|
185
|
+
if (requireAttorneyReview && !context.attorney_reviewer_id) {
|
|
186
|
+
return {
|
|
187
|
+
allow: false,
|
|
188
|
+
reasons: [
|
|
189
|
+
{
|
|
190
|
+
code: "oap.attorney_review_required",
|
|
191
|
+
message: "Attorney review is required but attorney_reviewer_id is missing",
|
|
192
|
+
severity: "error",
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check privilege protection
|
|
199
|
+
const privilegeProtectionEnabled =
|
|
200
|
+
passport.limits?.legal?.contract?.review?.privilege_protection_enabled;
|
|
201
|
+
if (privilegeProtectionEnabled && !context.privilege_level) {
|
|
202
|
+
return {
|
|
203
|
+
allow: false,
|
|
204
|
+
reasons: [
|
|
205
|
+
{
|
|
206
|
+
code: "oap.privilege_protection_violation",
|
|
207
|
+
message: "Privilege protection is enabled but privilege_level is missing",
|
|
208
|
+
severity: "error",
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check client tier authorization
|
|
215
|
+
const allowedClientTiers =
|
|
216
|
+
passport.limits?.legal?.contract?.review?.allowed_client_tiers || [];
|
|
217
|
+
if (
|
|
218
|
+
context.client_tier &&
|
|
219
|
+
allowedClientTiers.length > 0 &&
|
|
220
|
+
!allowedClientTiers.includes(context.client_tier)
|
|
221
|
+
) {
|
|
222
|
+
return {
|
|
223
|
+
allow: false,
|
|
224
|
+
reasons: [
|
|
225
|
+
{
|
|
226
|
+
code: "oap.client_tier_forbidden",
|
|
227
|
+
message: `Client tier ${context.client_tier} is not authorized`,
|
|
228
|
+
severity: "error",
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check idempotency
|
|
235
|
+
const idempotencyRequired =
|
|
236
|
+
passport.limits?.legal?.contract?.review?.idempotency_required ?? false;
|
|
237
|
+
if (idempotencyRequired && !context.idempotency_key) {
|
|
238
|
+
return {
|
|
239
|
+
allow: false,
|
|
240
|
+
reasons: [
|
|
241
|
+
{
|
|
242
|
+
code: "oap.idempotency_conflict",
|
|
243
|
+
message: "Idempotency key is required",
|
|
244
|
+
severity: "error",
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
allow: true,
|
|
252
|
+
reasons: [],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Test cases
|
|
257
|
+
describe("Legal Contract Review v1 Policy - Core Functionality", () => {
|
|
258
|
+
describe("Required Fields Validation", () => {
|
|
259
|
+
test("should allow contract review with all required fields", async () => {
|
|
260
|
+
const validContext = {
|
|
261
|
+
document_type: "contract",
|
|
262
|
+
client_id: "client_abc123",
|
|
263
|
+
jurisdiction: "US",
|
|
264
|
+
action_type: "review",
|
|
265
|
+
idempotency_key: "unique-key-12345",
|
|
266
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
267
|
+
privilege_level: "privileged",
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const result = await evaluateLegalContractReviewV1(
|
|
271
|
+
passportTemplate,
|
|
272
|
+
validContext
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(result.allow).toBe(true);
|
|
276
|
+
expect(result.reasons).toHaveLength(0);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("should deny contract review missing required fields", async () => {
|
|
280
|
+
const invalidContext = {
|
|
281
|
+
document_type: "contract",
|
|
282
|
+
// Missing client_id, jurisdiction, action_type, idempotency_key
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const result = await evaluateLegalContractReviewV1(
|
|
286
|
+
passportTemplate,
|
|
287
|
+
invalidContext
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
expect(result.allow).toBe(false);
|
|
291
|
+
expect(result.reasons[0].code).toBe("oap.invalid_context");
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe("Document Type Validation", () => {
|
|
296
|
+
test("should allow allowed document types", async () => {
|
|
297
|
+
const validContext = {
|
|
298
|
+
document_type: "contract",
|
|
299
|
+
client_id: "client_abc123",
|
|
300
|
+
jurisdiction: "US",
|
|
301
|
+
action_type: "review",
|
|
302
|
+
idempotency_key: "unique-key-12345",
|
|
303
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
304
|
+
privilege_level: "privileged",
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const result = await evaluateLegalContractReviewV1(
|
|
308
|
+
passportTemplate,
|
|
309
|
+
validContext
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
expect(result.allow).toBe(true);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("should deny forbidden document types", async () => {
|
|
316
|
+
const invalidContext = {
|
|
317
|
+
document_type: "forbidden_type",
|
|
318
|
+
client_id: "client_abc123",
|
|
319
|
+
jurisdiction: "US",
|
|
320
|
+
action_type: "review",
|
|
321
|
+
idempotency_key: "unique-key-12345",
|
|
322
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
323
|
+
privilege_level: "privileged",
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const result = await evaluateLegalContractReviewV1(
|
|
327
|
+
passportTemplate,
|
|
328
|
+
invalidContext
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
expect(result.allow).toBe(false);
|
|
332
|
+
expect(result.reasons[0].code).toBe("oap.document_type_forbidden");
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe("Document Size Limits", () => {
|
|
337
|
+
test("should allow documents within size limit", async () => {
|
|
338
|
+
const validContext = {
|
|
339
|
+
document_type: "contract",
|
|
340
|
+
client_id: "client_abc123",
|
|
341
|
+
jurisdiction: "US",
|
|
342
|
+
action_type: "review",
|
|
343
|
+
idempotency_key: "unique-key-12345",
|
|
344
|
+
document_size_mb: 5,
|
|
345
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
346
|
+
privilege_level: "privileged",
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const result = await evaluateLegalContractReviewV1(
|
|
350
|
+
passportTemplate,
|
|
351
|
+
validContext
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
expect(result.allow).toBe(true);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("should deny documents exceeding size limit", async () => {
|
|
358
|
+
const invalidContext = {
|
|
359
|
+
document_type: "contract",
|
|
360
|
+
client_id: "client_abc123",
|
|
361
|
+
jurisdiction: "US",
|
|
362
|
+
action_type: "review",
|
|
363
|
+
idempotency_key: "unique-key-12345",
|
|
364
|
+
document_size_mb: 15, // Exceeds 10MB limit
|
|
365
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
366
|
+
privilege_level: "privileged",
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const result = await evaluateLegalContractReviewV1(
|
|
370
|
+
passportTemplate,
|
|
371
|
+
invalidContext
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(result.allow).toBe(false);
|
|
375
|
+
expect(result.reasons[0].code).toBe("oap.document_size_exceeded");
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe("Jurisdiction Controls", () => {
|
|
380
|
+
test("should allow authorized jurisdictions", async () => {
|
|
381
|
+
const validContext = {
|
|
382
|
+
document_type: "contract",
|
|
383
|
+
client_id: "client_abc123",
|
|
384
|
+
jurisdiction: "US",
|
|
385
|
+
action_type: "review",
|
|
386
|
+
idempotency_key: "unique-key-12345",
|
|
387
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
388
|
+
privilege_level: "privileged",
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const result = await evaluateLegalContractReviewV1(
|
|
392
|
+
passportTemplate,
|
|
393
|
+
validContext
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
expect(result.allow).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test("should deny unauthorized jurisdictions", async () => {
|
|
400
|
+
const invalidContext = {
|
|
401
|
+
document_type: "contract",
|
|
402
|
+
client_id: "client_abc123",
|
|
403
|
+
jurisdiction: "FR", // Not in allowed list
|
|
404
|
+
action_type: "review",
|
|
405
|
+
idempotency_key: "unique-key-12345",
|
|
406
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
407
|
+
privilege_level: "privileged",
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const result = await evaluateLegalContractReviewV1(
|
|
411
|
+
passportTemplate,
|
|
412
|
+
invalidContext
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
expect(result.allow).toBe(false);
|
|
416
|
+
expect(result.reasons[0].code).toBe("oap.jurisdiction_blocked");
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
describe("Attorney Review Requirements", () => {
|
|
421
|
+
test("should require attorney review when configured", async () => {
|
|
422
|
+
const invalidContext = {
|
|
423
|
+
document_type: "contract",
|
|
424
|
+
client_id: "client_abc123",
|
|
425
|
+
jurisdiction: "US",
|
|
426
|
+
action_type: "review",
|
|
427
|
+
idempotency_key: "unique-key-12345",
|
|
428
|
+
// Missing attorney_reviewer_id
|
|
429
|
+
privilege_level: "privileged",
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const result = await evaluateLegalContractReviewV1(
|
|
433
|
+
passportTemplate,
|
|
434
|
+
invalidContext
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
expect(result.allow).toBe(false);
|
|
438
|
+
expect(result.reasons[0].code).toBe("oap.attorney_review_required");
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test("should require attorney review for high-value contracts", async () => {
|
|
442
|
+
const invalidContext = {
|
|
443
|
+
document_type: "contract",
|
|
444
|
+
client_id: "client_abc123",
|
|
445
|
+
jurisdiction: "US",
|
|
446
|
+
action_type: "review",
|
|
447
|
+
idempotency_key: "unique-key-12345",
|
|
448
|
+
contract_value_usd: 1500000, // $15,000 - over $10,000 threshold
|
|
449
|
+
privilege_level: "privileged",
|
|
450
|
+
// Missing attorney_reviewer_id
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const result = await evaluateLegalContractReviewV1(
|
|
454
|
+
passportTemplate,
|
|
455
|
+
invalidContext
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
expect(result.allow).toBe(false);
|
|
459
|
+
expect(result.reasons[0].code).toBe("oap.high_value_review_required");
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
describe("Privilege Protection", () => {
|
|
464
|
+
test("should require privilege level when protection is enabled", async () => {
|
|
465
|
+
const invalidContext = {
|
|
466
|
+
document_type: "contract",
|
|
467
|
+
client_id: "client_abc123",
|
|
468
|
+
jurisdiction: "US",
|
|
469
|
+
action_type: "review",
|
|
470
|
+
idempotency_key: "unique-key-12345",
|
|
471
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
472
|
+
// Missing privilege_level
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const result = await evaluateLegalContractReviewV1(
|
|
476
|
+
passportTemplate,
|
|
477
|
+
invalidContext
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
expect(result.allow).toBe(false);
|
|
481
|
+
expect(result.reasons[0].code).toBe("oap.privilege_protection_violation");
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
describe("Client Tier Authorization", () => {
|
|
486
|
+
test("should allow authorized client tiers", async () => {
|
|
487
|
+
const validContext = {
|
|
488
|
+
document_type: "contract",
|
|
489
|
+
client_id: "client_abc123",
|
|
490
|
+
jurisdiction: "US",
|
|
491
|
+
action_type: "review",
|
|
492
|
+
idempotency_key: "unique-key-12345",
|
|
493
|
+
client_tier: "tier1",
|
|
494
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
495
|
+
privilege_level: "privileged",
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const result = await evaluateLegalContractReviewV1(
|
|
499
|
+
passportTemplate,
|
|
500
|
+
validContext
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
expect(result.allow).toBe(true);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
test("should deny unauthorized client tiers", async () => {
|
|
507
|
+
const invalidContext = {
|
|
508
|
+
document_type: "contract",
|
|
509
|
+
client_id: "client_abc123",
|
|
510
|
+
jurisdiction: "US",
|
|
511
|
+
action_type: "review",
|
|
512
|
+
idempotency_key: "unique-key-12345",
|
|
513
|
+
client_tier: "tier4", // Not in allowed list
|
|
514
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
515
|
+
privilege_level: "privileged",
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const result = await evaluateLegalContractReviewV1(
|
|
519
|
+
passportTemplate,
|
|
520
|
+
invalidContext
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
expect(result.allow).toBe(false);
|
|
524
|
+
expect(result.reasons[0].code).toBe("oap.client_tier_forbidden");
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
describe("Capability Checks", () => {
|
|
529
|
+
test("should deny if agent lacks legal.contract.review capability", async () => {
|
|
530
|
+
const passportWithoutCapability = {
|
|
531
|
+
...passportTemplate,
|
|
532
|
+
capabilities: [],
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
const validContext = {
|
|
536
|
+
document_type: "contract",
|
|
537
|
+
client_id: "client_abc123",
|
|
538
|
+
jurisdiction: "US",
|
|
539
|
+
action_type: "review",
|
|
540
|
+
idempotency_key: "unique-key-12345",
|
|
541
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
542
|
+
privilege_level: "privileged",
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const result = await evaluateLegalContractReviewV1(
|
|
546
|
+
passportWithoutCapability,
|
|
547
|
+
validContext
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
expect(result.allow).toBe(false);
|
|
551
|
+
expect(result.reasons[0].code).toBe("oap.unknown_capability");
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
describe("Assurance Level Checks", () => {
|
|
556
|
+
test("should deny if assurance level is insufficient", async () => {
|
|
557
|
+
const passportWithLowAssurance = {
|
|
558
|
+
...passportTemplate,
|
|
559
|
+
assurance_level: "L2", // Below required L3
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
const validContext = {
|
|
563
|
+
document_type: "contract",
|
|
564
|
+
client_id: "client_abc123",
|
|
565
|
+
jurisdiction: "US",
|
|
566
|
+
action_type: "review",
|
|
567
|
+
idempotency_key: "unique-key-12345",
|
|
568
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
569
|
+
privilege_level: "privileged",
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
const result = await evaluateLegalContractReviewV1(
|
|
573
|
+
passportWithLowAssurance,
|
|
574
|
+
validContext
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
expect(result.allow).toBe(false);
|
|
578
|
+
expect(result.reasons[0].code).toBe("oap.assurance_insufficient");
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
describe("Agent Status Checks", () => {
|
|
583
|
+
test("should deny if agent is suspended", async () => {
|
|
584
|
+
const suspendedPassport = {
|
|
585
|
+
...passportTemplate,
|
|
586
|
+
status: "suspended",
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
const validContext = {
|
|
590
|
+
document_type: "contract",
|
|
591
|
+
client_id: "client_abc123",
|
|
592
|
+
jurisdiction: "US",
|
|
593
|
+
action_type: "review",
|
|
594
|
+
idempotency_key: "unique-key-12345",
|
|
595
|
+
attorney_reviewer_id: "attorney_xyz789",
|
|
596
|
+
privilege_level: "privileged",
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
const result = await evaluateLegalContractReviewV1(
|
|
600
|
+
suspendedPassport,
|
|
601
|
+
validContext
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
expect(result.allow).toBe(false);
|
|
605
|
+
expect(result.reasons[0].code).toBe("oap.passport_suspended");
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent_id": "ap_legal_contract_review_template",
|
|
3
|
+
"slug": "legal-contract-review-template",
|
|
4
|
+
"name": "Legal Contract Review Template",
|
|
5
|
+
"kind": "template",
|
|
6
|
+
"spec_version": "oap/1.0",
|
|
7
|
+
"owner_id": "org_law_firm_demo",
|
|
8
|
+
"owner_type": "org",
|
|
9
|
+
"owner_display": "Demo Law Firm",
|
|
10
|
+
"controller_type": "org",
|
|
11
|
+
"claimed": true,
|
|
12
|
+
"assurance_level": "L3",
|
|
13
|
+
"status": "active",
|
|
14
|
+
"capabilities": [
|
|
15
|
+
{
|
|
16
|
+
"id": "legal.contract.review",
|
|
17
|
+
"params": {}
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"limits": {
|
|
21
|
+
"legal": {
|
|
22
|
+
"contract": {
|
|
23
|
+
"review": {
|
|
24
|
+
"allowed_document_types": [
|
|
25
|
+
"contract",
|
|
26
|
+
"nda",
|
|
27
|
+
"msa",
|
|
28
|
+
"sla",
|
|
29
|
+
"employment_agreement"
|
|
30
|
+
],
|
|
31
|
+
"max_document_size_mb": 10,
|
|
32
|
+
"allowed_contract_jurisdictions": ["US", "CA", "GB"],
|
|
33
|
+
"require_attorney_review": true,
|
|
34
|
+
"privilege_protection_enabled": true,
|
|
35
|
+
"max_contracts_per_day": 50,
|
|
36
|
+
"allowed_client_tiers": ["tier1", "tier2", "enterprise"],
|
|
37
|
+
"require_assurance_at_least": "L3",
|
|
38
|
+
"idempotency_required": true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"regions": ["US", "CA", "GB"],
|
|
44
|
+
"role": "legal_assistant",
|
|
45
|
+
"description": "AI agent for legal contract review operations",
|
|
46
|
+
"created_at": "2025-01-30T00:00:00Z",
|
|
47
|
+
"updated_at": "2025-01-30T00:00:00Z",
|
|
48
|
+
"version": "1.0.0"
|
|
49
|
+
}
|