@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.
Files changed (237) hide show
  1. package/LICENSE +217 -0
  2. package/README.md +481 -0
  3. package/bin/agent-guardrails +133 -0
  4. package/bin/aport-create-passport.sh +444 -0
  5. package/bin/aport-cursor-hook.sh +90 -0
  6. package/bin/aport-guardrail-api.sh +108 -0
  7. package/bin/aport-guardrail-bash.sh +394 -0
  8. package/bin/aport-guardrail-v2.sh +5 -0
  9. package/bin/aport-guardrail.sh +5 -0
  10. package/bin/aport-resolve-paths.sh +71 -0
  11. package/bin/aport-status.sh +276 -0
  12. package/bin/frameworks/crewai.sh +49 -0
  13. package/bin/frameworks/cursor.sh +95 -0
  14. package/bin/frameworks/langchain.sh +48 -0
  15. package/bin/frameworks/n8n.sh +36 -0
  16. package/bin/frameworks/openclaw.sh +19 -0
  17. package/bin/lib/allowlist.sh +18 -0
  18. package/bin/lib/common.sh +28 -0
  19. package/bin/lib/config.sh +46 -0
  20. package/bin/lib/constants.sh +232 -0
  21. package/bin/lib/detect.sh +65 -0
  22. package/bin/lib/error.sh +269 -0
  23. package/bin/lib/passport.sh +19 -0
  24. package/bin/lib/templates/.gitkeep +1 -0
  25. package/bin/lib/templates/config.yaml +6 -0
  26. package/bin/lib/validation.sh +206 -0
  27. package/bin/openclaw +660 -0
  28. package/docs/ADDING_A_FRAMEWORK.md +87 -0
  29. package/docs/AGENTS.md.example +40 -0
  30. package/docs/CODE_REVIEW.md +192 -0
  31. package/docs/DEPLOYMENT_READINESS.md +81 -0
  32. package/docs/FAQ_SECURITY_SCANNERS.md +373 -0
  33. package/docs/FRAMEWORK_ROADMAP.md +41 -0
  34. package/docs/HOSTED_PASSPORT_SETUP.md +362 -0
  35. package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +433 -0
  36. package/docs/OPENCLAW_COMPATIBILITY.md +73 -0
  37. package/docs/OPENCLAW_LOCAL_INTEGRATION.md +596 -0
  38. package/docs/OPENCLAW_TOOLS_AND_POLICIES.md +54 -0
  39. package/docs/QUICKSTART.md +470 -0
  40. package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +470 -0
  41. package/docs/README.md +28 -0
  42. package/docs/RELEASE.md +87 -0
  43. package/docs/REPO_LAYOUT.md +47 -0
  44. package/docs/SKILLS_ECOSYSTEM_ANALYSIS_FEB17.md +1260 -0
  45. package/docs/TOOL_POLICY_MAPPING.md +46 -0
  46. package/docs/UPGRADE.md +46 -0
  47. package/docs/VERIFICATION_METHODS.md +97 -0
  48. package/docs/assets/README.md +8 -0
  49. package/docs/assets/porter.svg +54 -0
  50. package/docs/development/ERROR_CODES.md +616 -0
  51. package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +1105 -0
  52. package/docs/frameworks/crewai.md +114 -0
  53. package/docs/frameworks/cursor.md +159 -0
  54. package/docs/frameworks/langchain.md +72 -0
  55. package/docs/frameworks/n8n.md +40 -0
  56. package/docs/frameworks/openclaw.md +40 -0
  57. package/docs/launch/ADD_APORT_AWESOME_LISTS_INSTRUCTIONS.md +146 -0
  58. package/docs/launch/ANNOUNCEMENT_GUIDE.md +266 -0
  59. package/docs/launch/AWESOME_REPOS.md +53 -0
  60. package/docs/launch/CURSOR_VSCODE_HOOKS_RESEARCH.md +77 -0
  61. package/docs/launch/DEMO_TERMINAL_OUTPUT.txt +48 -0
  62. package/docs/launch/DRY_AND_PLAN_CHECKLIST.md +47 -0
  63. package/docs/launch/EVIDENCE_README.md +61 -0
  64. package/docs/launch/EVIDENCE_TERMINAL_CAPTURE.txt +10 -0
  65. package/docs/launch/FRAMEWORK_SUPPORT_PLAN.md +1640 -0
  66. package/docs/launch/LAUNCH_READINESS_CHECKLIST.md +237 -0
  67. package/docs/launch/LAUNCH_STRATEGY_SUMMARY.md +464 -0
  68. package/docs/launch/OPENCLAW_FEEDBACK_AND_FIXES.md +85 -0
  69. package/docs/launch/POST_1_VALENTINE_IMPROVED.md +233 -0
  70. package/docs/launch/POST_2_GUARDRAIL_IMPROVED.md +369 -0
  71. package/docs/launch/PRE_LAUNCH_FIXES.md +766 -0
  72. package/docs/launch/QUICK_LAUNCH_CHECKLIST.md +400 -0
  73. package/docs/launch/READINESS_SUMMARY.md +262 -0
  74. package/docs/launch/README.md +68 -0
  75. package/docs/launch/USER_STORIES.md +327 -0
  76. package/docs/launch/scripts/add-aport-awesome-pr.sh +69 -0
  77. package/docs/operations/MONITORING.md +588 -0
  78. package/docs/reviews/2026-02-18-staff-review.md +268 -0
  79. package/extensions/openclaw-aport/README.md +415 -0
  80. package/extensions/openclaw-aport/index.js +625 -0
  81. package/extensions/openclaw-aport/openclaw-aport.js +7 -0
  82. package/extensions/openclaw-aport/openclaw.plugin.json +46 -0
  83. package/extensions/openclaw-aport/package.json +36 -0
  84. package/extensions/openclaw-aport/test.js +307 -0
  85. package/external/aport-policies/README.md +363 -0
  86. package/external/aport-policies/agent.session.create.v1/README.md +345 -0
  87. package/external/aport-policies/agent.session.create.v1/policy.json +162 -0
  88. package/external/aport-policies/agent.tool.register.v1/README.md +361 -0
  89. package/external/aport-policies/agent.tool.register.v1/policy.json +172 -0
  90. package/external/aport-policies/code.release.publish.v1/README.md +51 -0
  91. package/external/aport-policies/code.release.publish.v1/policy.json +121 -0
  92. package/external/aport-policies/code.repository.merge.v1/README.md +287 -0
  93. package/external/aport-policies/code.repository.merge.v1/express.example.js +332 -0
  94. package/external/aport-policies/code.repository.merge.v1/fastapi.example.py +370 -0
  95. package/external/aport-policies/code.repository.merge.v1/policy.json +162 -0
  96. package/external/aport-policies/data.export.create.v1/README.md +226 -0
  97. package/external/aport-policies/data.export.create.v1/express.example.js +172 -0
  98. package/external/aport-policies/data.export.create.v1/fastapi.example.py +165 -0
  99. package/external/aport-policies/data.export.create.v1/policy.json +133 -0
  100. package/external/aport-policies/data.report.ingest.v1/README.md +134 -0
  101. package/external/aport-policies/data.report.ingest.v1/express.example.js +105 -0
  102. package/external/aport-policies/data.report.ingest.v1/minimal-example.js +68 -0
  103. package/external/aport-policies/data.report.ingest.v1/policy.json +174 -0
  104. package/external/aport-policies/finance.crypto.trade.v1/README.md +146 -0
  105. package/external/aport-policies/finance.crypto.trade.v1/express.example.js +109 -0
  106. package/external/aport-policies/finance.crypto.trade.v1/minimal-example.js +65 -0
  107. package/external/aport-policies/finance.crypto.trade.v1/policy.json +176 -0
  108. package/external/aport-policies/finance.payment.charge.v1/README.md +326 -0
  109. package/external/aport-policies/finance.payment.charge.v1/express.example.js +250 -0
  110. package/external/aport-policies/finance.payment.charge.v1/fastapi.example.py +227 -0
  111. package/external/aport-policies/finance.payment.charge.v1/minimal-example.js +64 -0
  112. package/external/aport-policies/finance.payment.charge.v1/policy.json +224 -0
  113. package/external/aport-policies/finance.payment.charge.v1/tests/contexts.jsonl +12 -0
  114. package/external/aport-policies/finance.payment.charge.v1/tests/expected.jsonl +12 -0
  115. package/external/aport-policies/finance.payment.charge.v1/tests/passport.instance.json +42 -0
  116. package/external/aport-policies/finance.payment.charge.v1/tests/passport.template.json +40 -0
  117. package/external/aport-policies/finance.payment.charge.v1/tests/payments-charge-policy.test.js +817 -0
  118. package/external/aport-policies/finance.payment.charge.v1/tests/test_payments_charge_policy.py +486 -0
  119. package/external/aport-policies/finance.payment.payout.v1/README.md +78 -0
  120. package/external/aport-policies/finance.payment.payout.v1/policy.json +181 -0
  121. package/external/aport-policies/finance.payment.refund.v1/README.md +275 -0
  122. package/external/aport-policies/finance.payment.refund.v1/express.example.js +167 -0
  123. package/external/aport-policies/finance.payment.refund.v1/fastapi.example.py +136 -0
  124. package/external/aport-policies/finance.payment.refund.v1/minimal-example.js +183 -0
  125. package/external/aport-policies/finance.payment.refund.v1/policy.json +216 -0
  126. package/external/aport-policies/finance.payment.refund.v1/tests/refunds-policy.test.js +924 -0
  127. package/external/aport-policies/finance.payment.refund.v1/tests/test_refunds_policy.py +778 -0
  128. package/external/aport-policies/finance.transaction.execute.v1/README.md +309 -0
  129. package/external/aport-policies/finance.transaction.execute.v1/express.example.js +261 -0
  130. package/external/aport-policies/finance.transaction.execute.v1/fastapi.example.py +231 -0
  131. package/external/aport-policies/finance.transaction.execute.v1/minimal-example.js +78 -0
  132. package/external/aport-policies/finance.transaction.execute.v1/policy.json +189 -0
  133. package/external/aport-policies/finance.transaction.execute.v1/tests/contexts.jsonl +12 -0
  134. package/external/aport-policies/finance.transaction.execute.v1/tests/expected.jsonl +12 -0
  135. package/external/aport-policies/finance.transaction.execute.v1/tests/passport.instance.json +42 -0
  136. package/external/aport-policies/finance.transaction.execute.v1/tests/passport.template.json +42 -0
  137. package/external/aport-policies/finance.transaction.execute.v1/tests/test_transactions_policy.py +214 -0
  138. package/external/aport-policies/finance.transaction.execute.v1/tests/transactions-policy.test.js +306 -0
  139. package/external/aport-policies/governance.data.access.v1/README.md +292 -0
  140. package/external/aport-policies/governance.data.access.v1/express.example.js +321 -0
  141. package/external/aport-policies/governance.data.access.v1/fastapi.example.py +279 -0
  142. package/external/aport-policies/governance.data.access.v1/minimal-example.js +65 -0
  143. package/external/aport-policies/governance.data.access.v1/policy.json +208 -0
  144. package/external/aport-policies/governance.data.access.v1/tests/contexts.jsonl +12 -0
  145. package/external/aport-policies/governance.data.access.v1/tests/data-access-policy.test.js +308 -0
  146. package/external/aport-policies/governance.data.access.v1/tests/expected.jsonl +12 -0
  147. package/external/aport-policies/governance.data.access.v1/tests/passport.instance.json +56 -0
  148. package/external/aport-policies/governance.data.access.v1/tests/passport.template.json +56 -0
  149. package/external/aport-policies/governance.data.access.v1/tests/test_data_access_policy.py +214 -0
  150. package/external/aport-policies/legal.contract.review.v1/README.md +109 -0
  151. package/external/aport-policies/legal.contract.review.v1/policy.json +378 -0
  152. package/external/aport-policies/legal.contract.review.v1/tests/legal-contract-review-policy.test.js +609 -0
  153. package/external/aport-policies/legal.contract.review.v1/tests/passport.template.json +49 -0
  154. package/external/aport-policies/mcp.tool.execute.v1/README.md +301 -0
  155. package/external/aport-policies/mcp.tool.execute.v1/policy.json +141 -0
  156. package/external/aport-policies/messaging.message.send.v1/README.md +230 -0
  157. package/external/aport-policies/messaging.message.send.v1/express.example.js +183 -0
  158. package/external/aport-policies/messaging.message.send.v1/fastapi.example.py +193 -0
  159. package/external/aport-policies/messaging.message.send.v1/policy.json +144 -0
  160. package/external/aport-policies/policy-template.json +107 -0
  161. package/external/aport-policies/system.command.execute.v1/README.md +275 -0
  162. package/external/aport-policies/system.command.execute.v1/policy.json +146 -0
  163. package/external/aport-spec/CONTRIBUTING.md +273 -0
  164. package/external/aport-spec/LICENSE +21 -0
  165. package/external/aport-spec/README.md +168 -0
  166. package/external/aport-spec/conformance/README.md +294 -0
  167. package/external/aport-spec/conformance/cases/data.export.v1/contexts/allow_users.json +6 -0
  168. package/external/aport-spec/conformance/cases/data.export.v1/contexts/deny_pii.json +6 -0
  169. package/external/aport-spec/conformance/cases/data.export.v1/expected/allow_users.decision.json +19 -0
  170. package/external/aport-spec/conformance/cases/data.export.v1/expected/deny_pii.decision.json +19 -0
  171. package/external/aport-spec/conformance/cases/data.export.v1/passports/template.json +29 -0
  172. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/allow_50usd.json +9 -0
  173. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_150usd.json +9 -0
  174. package/external/aport-spec/conformance/cases/payments.refunds.v1/contexts/deny_currency.json +9 -0
  175. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/allow_50usd.decision.json +19 -0
  176. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_150usd.decision.json +19 -0
  177. package/external/aport-spec/conformance/cases/payments.refunds.v1/expected/deny_currency.decision.json +19 -0
  178. package/external/aport-spec/conformance/cases/payments.refunds.v1/passports/template.json +42 -0
  179. package/external/aport-spec/conformance/package.json +44 -0
  180. package/external/aport-spec/conformance/pnpm-lock.yaml +642 -0
  181. package/external/aport-spec/conformance/src/cases.ts +371 -0
  182. package/external/aport-spec/conformance/src/ed25519.ts +167 -0
  183. package/external/aport-spec/conformance/src/jcs.ts +85 -0
  184. package/external/aport-spec/conformance/src/runner.ts +533 -0
  185. package/external/aport-spec/conformance/src/validators.ts +185 -0
  186. package/external/aport-spec/conformance/test-runner.js +315 -0
  187. package/external/aport-spec/conformance/tsconfig.json +21 -0
  188. package/external/aport-spec/error-schema.json +192 -0
  189. package/external/aport-spec/index.json +12 -0
  190. package/external/aport-spec/integrations/clawmoat/README.md +12 -0
  191. package/external/aport-spec/integrations/shield/README.md +245 -0
  192. package/external/aport-spec/integrations/shield/adapters/index.js +116 -0
  193. package/external/aport-spec/integrations/shield/adapters/system-command-execute.js +133 -0
  194. package/external/aport-spec/integrations/shield/test/README.md +58 -0
  195. package/external/aport-spec/integrations/shield/test/shield.md +40 -0
  196. package/external/aport-spec/integrations/shield/test/test-shield-to-verify.js +274 -0
  197. package/external/aport-spec/metrics-schema.json +504 -0
  198. package/external/aport-spec/oap/CHANGELOG.md +54 -0
  199. package/external/aport-spec/oap/VERSION.md +40 -0
  200. package/external/aport-spec/oap/capability-registry.md +229 -0
  201. package/external/aport-spec/oap/conformance.md +257 -0
  202. package/external/aport-spec/oap/decision-schema.json +114 -0
  203. package/external/aport-spec/oap/examples/context.refund.usd.50.json +9 -0
  204. package/external/aport-spec/oap/examples/decision.allow.sample.json +20 -0
  205. package/external/aport-spec/oap/examples/decision.deny.sample.json +23 -0
  206. package/external/aport-spec/oap/examples/passport.instance.v1.json +50 -0
  207. package/external/aport-spec/oap/examples/passport.template.v1.json +71 -0
  208. package/external/aport-spec/oap/oap-spec.md +426 -0
  209. package/external/aport-spec/oap/passport-schema.json +396 -0
  210. package/external/aport-spec/oap/security.md +213 -0
  211. package/external/aport-spec/oap/vc/context-oap-v1.jsonld +137 -0
  212. package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +37 -0
  213. package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +68 -0
  214. package/external/aport-spec/oap/vc/tools/INTEGRATION.md +375 -0
  215. package/external/aport-spec/oap/vc/tools/README.md +278 -0
  216. package/external/aport-spec/oap/vc/tools/examples/decision-to-vc.js +66 -0
  217. package/external/aport-spec/oap/vc/tools/examples/passport-to-vc.js +83 -0
  218. package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +77 -0
  219. package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +94 -0
  220. package/external/aport-spec/oap/vc/tools/package.json +38 -0
  221. package/external/aport-spec/oap/vc/tools/pnpm-lock.yaml +472 -0
  222. package/external/aport-spec/oap/vc/tools/src/cli.ts +226 -0
  223. package/external/aport-spec/oap/vc/tools/src/crypto-utils.ts +427 -0
  224. package/external/aport-spec/oap/vc/tools/src/index.ts +653 -0
  225. package/external/aport-spec/oap/vc/tools/src/test.ts +148 -0
  226. package/external/aport-spec/oap/vc/tools/src/vp.ts +382 -0
  227. package/external/aport-spec/oap/vc/tools/test-simple.js +214 -0
  228. package/external/aport-spec/oap/vc/tools/tsconfig.json +19 -0
  229. package/external/aport-spec/oap/vc/vc-mapping.md +443 -0
  230. package/external/aport-spec/passport-schema.json +586 -0
  231. package/external/aport-spec/rate-limiting.md +136 -0
  232. package/external/aport-spec/transport-profile.md +325 -0
  233. package/external/aport-spec/webhook-spec.md +314 -0
  234. package/package.json +70 -0
  235. package/skills/aport-agent-guardrail/SKILL.md +314 -0
  236. package/src/evaluator.js +252 -0
  237. package/src/server/index.js +72 -0
@@ -0,0 +1,162 @@
1
+ {
2
+ "id": "code.repository.merge.v1",
3
+ "name": "Repository Safety Policy",
4
+ "description": "Pre-action governance for repository operations. Enforces PR limits, merge controls, path restrictions, and review requirements for dev-first safety.",
5
+ "version": "1.0.0",
6
+ "status": "active",
7
+ "requires_capabilities": ["repo.pr.create", "repo.merge"],
8
+ "min_assurance": "L2",
9
+ "limits_required": [
10
+ "max_prs_per_day",
11
+ "max_merges_per_day",
12
+ "max_pr_size_kb"
13
+ ],
14
+ "required_fields": ["repository", "action", "branch"],
15
+ "optional_fields": [
16
+ "base_branch",
17
+ "title",
18
+ "description",
19
+ "files_changed",
20
+ "lines_added",
21
+ "lines_removed"
22
+ ],
23
+ "enforcement": {
24
+ "allowed_repos_enforced": true,
25
+ "allowed_base_branches_enforced": true,
26
+ "path_allowlist_enforced": true,
27
+ "size_limits_enforced": true,
28
+ "review_requirements_enforced": true
29
+ },
30
+ "mcp": {
31
+ "require_allowlisted_if_present": true
32
+ },
33
+ "advice": [
34
+ "Implement repository allowlists to prevent unauthorized access",
35
+ "Use branch protection rules for critical branches",
36
+ "Monitor PR size and complexity to prevent oversized changes",
37
+ "Require code reviews for production merges",
38
+ "Log all repository operations for Verifiable Attestation",
39
+ "Use path allowlists to restrict file access patterns",
40
+ "Subscribe to status webhooks for instant suspend",
41
+ "Implement progressive limits for new agents",
42
+ "Use automated testing requirements for large changes"
43
+ ],
44
+ "required_context": {
45
+ "$schema": "http://json-schema.org/draft-07/schema#",
46
+ "type": "object",
47
+ "required": ["repository", "action", "branch"],
48
+ "properties": {
49
+ "repository": {
50
+ "type": "string",
51
+ "pattern": "^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$",
52
+ "description": "Repository in owner/repo format"
53
+ },
54
+ "action": {
55
+ "type": "string",
56
+ "enum": [
57
+ "pr.create",
58
+ "pr.merge",
59
+ "pr.update",
60
+ "branch.create",
61
+ "branch.delete"
62
+ ],
63
+ "description": "Repository action being performed"
64
+ },
65
+ "branch": {
66
+ "type": "string",
67
+ "minLength": 1,
68
+ "description": "Target branch name"
69
+ },
70
+ "base_branch": {
71
+ "type": "string",
72
+ "description": "Base branch for PR operations"
73
+ },
74
+ "title": {
75
+ "type": "string",
76
+ "maxLength": 200,
77
+ "description": "PR or commit title"
78
+ },
79
+ "description": {
80
+ "type": "string",
81
+ "maxLength": 5000,
82
+ "description": "PR or commit description"
83
+ },
84
+ "files_changed": {
85
+ "type": "array",
86
+ "items": { "type": "string" },
87
+ "description": "List of files being changed"
88
+ },
89
+ "lines_added": {
90
+ "type": "integer",
91
+ "minimum": 0,
92
+ "description": "Number of lines added"
93
+ },
94
+ "lines_removed": {
95
+ "type": "integer",
96
+ "minimum": 0,
97
+ "description": "Number of lines removed"
98
+ },
99
+ "mcp_servers": {
100
+ "type": "array",
101
+ "items": { "type": "string" },
102
+ "description": "MCP servers being used in this request (e.g., [\"https://mcp.github.com\"])"
103
+ },
104
+ "mcp_tools": {
105
+ "type": "array",
106
+ "items": { "type": "string" },
107
+ "description": "MCP tools being used in this request (e.g., [\"github.pull_requests.create\"])"
108
+ },
109
+ "mcp_server": {
110
+ "type": "string",
111
+ "description": "Single MCP server being used (backward compatibility - use mcp_servers array for multiple)"
112
+ },
113
+ "mcp_tool": {
114
+ "type": "string",
115
+ "description": "Single MCP tool being used (backward compatibility - use mcp_tools array for multiple)"
116
+ },
117
+ "mcp_session": {
118
+ "type": "string",
119
+ "description": "MCP session identifier for audit trail (optional)"
120
+ }
121
+ }
122
+ },
123
+ "evaluation_rules": [
124
+ {
125
+ "name": "passport_status_active",
126
+ "condition": "passport.status == 'active'",
127
+ "deny_code": "oap.passport_suspended",
128
+ "description": "Passport must be active"
129
+ },
130
+ {
131
+ "name": "repo_capability",
132
+ "condition": "action in passport.capabilities",
133
+ "deny_code": "oap.unknown_capability",
134
+ "description": "Agent must have required repository capability"
135
+ },
136
+ {
137
+ "name": "pr_size_limit",
138
+ "condition": "lines_added + lines_removed <= limits.max_pr_size_kb * 1000",
139
+ "deny_code": "oap.limit_exceeded",
140
+ "description": "PR size must not exceed limit"
141
+ },
142
+ {
143
+ "name": "daily_pr_limit",
144
+ "condition": "daily_pr_count < limits.max_prs_per_day",
145
+ "deny_code": "oap.limit_exceeded",
146
+ "description": "Daily PR limit must not be exceeded"
147
+ },
148
+ {
149
+ "name": "daily_merge_limit",
150
+ "condition": "daily_merge_count < limits.max_merges_per_day",
151
+ "deny_code": "oap.limit_exceeded",
152
+ "description": "Daily merge limit must not be exceeded"
153
+ }
154
+ ],
155
+ "cache": {
156
+ "default_ttl_seconds": 60,
157
+ "suspend_invalidate_seconds": 30
158
+ },
159
+ "deprecation": null,
160
+ "created_at": "2025-01-16T00:00:00Z",
161
+ "updated_at": "2025-01-30T00:00:00Z"
162
+ }
@@ -0,0 +1,226 @@
1
+ # Data Export Protection Policy Pack v1
2
+
3
+ Protect your data export endpoints with APort's standardized policy pack. This pack ensures only verified agents with proper capabilities, row limits, and PII handling can export sensitive data.
4
+
5
+ ## What This Pack Protects
6
+
7
+ - **Route**: `/exports/*` (GET, POST)
8
+ - **Risk**: Data breaches, privacy violations, regulatory non-compliance
9
+ - **Impact**: Customer data exposure, GDPR/CCPA violations, reputation damage
10
+
11
+ ## Requirements
12
+
13
+ | Requirement | Value | Description |
14
+ |-------------|-------|-------------|
15
+ | **Capability** | `data.export` | Agent must have data export capability |
16
+ | **Assurance** | `L1` or higher | Email verification minimum |
17
+ | **Limits** | `max_export_rows` | Maximum number of rows per export |
18
+ | **Limits** | `allow_pii` | Whether PII can be included in exports |
19
+
20
+ ## Implementation
21
+
22
+ ### Express.js
23
+
24
+ ```javascript
25
+ const { requirePolicy } = require('@aporthq/middleware-express');
26
+
27
+ app.post('/exports', requirePolicy('data.export.create.v1'), async (req, res) => {
28
+ // Your export logic here
29
+ // req.policyResult contains the verified passport
30
+ const { format, include_pii } = req.body;
31
+ const passport = req.policyResult.passport;
32
+
33
+ // Check PII permission
34
+ if (include_pii && !passport.limits.allow_pii) {
35
+ return res.status(403).json({ error: 'PII export not allowed' });
36
+ }
37
+
38
+ // Process export...
39
+ res.json({ success: true, export_id: generateId() });
40
+ });
41
+ ```
42
+
43
+ ### FastAPI
44
+
45
+ ```python
46
+ from aport.middleware import require_policy
47
+
48
+ @app.post("/exports")
49
+ @require_policy("data.export.create.v1")
50
+ async def create_export(request: Request, export_data: ExportRequest):
51
+ # Your export logic here
52
+ # request.state.policy_result contains the verified passport
53
+ passport = request.state.policy_result.passport
54
+
55
+ # Check PII permission
56
+ if export_data.include_pii and not passport.limits.allow_pii:
57
+ raise HTTPException(status_code=403, detail="PII export not allowed")
58
+
59
+ # Process export...
60
+ return {"success": True, "export_id": generate_id()}
61
+ ```
62
+
63
+ ## Error Responses
64
+
65
+ When policy checks fail, you'll receive a `403 Forbidden` with detailed error information:
66
+
67
+ ```json
68
+ {
69
+ "error": "policy_violation",
70
+ "code": "EXCEEDS_ROW_LIMIT",
71
+ "message": "Requested 10000 rows exceeds limit 5000",
72
+ "policy_id": "data.export.create.v1",
73
+ "agent_id": "ap_a2d10232c6534523812423eec8a1425c45678",
74
+ "upgrade_instructions": "Request smaller export or upgrade limits"
75
+ }
76
+ ```
77
+
78
+ ## Test Payloads
79
+
80
+ ### Valid Request (CSV export)
81
+ ```json
82
+ {
83
+ "format": "csv",
84
+ "include_pii": false,
85
+ "filters": {
86
+ "date_range": "2024-01-01 to 2024-12-31"
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Valid Request (with PII)
92
+ ```json
93
+ {
94
+ "format": "json",
95
+ "include_pii": true,
96
+ "filters": {
97
+ "user_id": "12345"
98
+ }
99
+ }
100
+ ```
101
+ *Requires `allow_pii: true` in agent limits*
102
+
103
+ ### Invalid Request (exceeds row limit)
104
+ ```json
105
+ {
106
+ "format": "csv",
107
+ "include_pii": false,
108
+ "filters": {
109
+ "date_range": "2020-01-01 to 2024-12-31"
110
+ }
111
+ }
112
+ ```
113
+ *Returns 403: "Requested 50000 rows exceeds limit 10000"*
114
+
115
+ ## Best Practices
116
+
117
+ 1. **Cache Verification**: Cache `/verify` responses with ETag for 60 seconds
118
+ 2. **Webhook Integration**: Subscribe to `status.changed` webhooks for instant suspension
119
+ 3. **Data Retention**: Implement data retention policies for exported files
120
+ 4. **Verifiable Attestation**: Log all export attempts for compliance
121
+ 5. **PII Handling**: Clearly separate PII and non-PII export capabilities
122
+
123
+ ## Compliance Badge
124
+
125
+ Agents that meet this policy's requirements can display the "Data Export-Ready" badge:
126
+
127
+ ```markdown
128
+ [![Data Export-Ready](https://api.aport.io/badge/ap_a2d10232c6534523812423eec8a1425c45678.svg)](https://aport.io/agents/ap_a2d10232c6534523812423eec8a1425c45678)
129
+ ```
130
+
131
+ ## Support
132
+
133
+ - [Documentation](https://aport.io/docs/policies/data.export.create.v1)
134
+ - [Community](https://github.com/aporthq/community)
135
+ - [Support](https://aport.io/support)
136
+
137
+
138
+ ## Required Context
139
+
140
+ This policy requires the following context (JSON Schema):
141
+
142
+ ```json
143
+ {
144
+ "$schema": "http://json-schema.org/draft-07/schema#",
145
+ "type": "object",
146
+ "required": [
147
+ "export_type",
148
+ "format",
149
+ "filters"
150
+ ],
151
+ "properties": {
152
+ "export_type": {
153
+ "type": "string",
154
+ "enum": [
155
+ "users",
156
+ "orders",
157
+ "transactions",
158
+ "analytics"
159
+ ],
160
+ "description": "Type of data to export"
161
+ },
162
+ "format": {
163
+ "type": "string",
164
+ "enum": [
165
+ "csv",
166
+ "json",
167
+ "xlsx",
168
+ "parquet"
169
+ ],
170
+ "description": "Export format"
171
+ },
172
+ "filters": {
173
+ "type": "object",
174
+ "description": "Filter criteria for the export",
175
+ "properties": {
176
+ "date_from": {
177
+ "type": "string",
178
+ "format": "date"
179
+ },
180
+ "date_to": {
181
+ "type": "string",
182
+ "format": "date"
183
+ },
184
+ "status": {
185
+ "type": "string"
186
+ },
187
+ "category": {
188
+ "type": "string"
189
+ }
190
+ }
191
+ },
192
+ "include_pii": {
193
+ "type": "boolean",
194
+ "description": "Whether to include personally identifiable information"
195
+ },
196
+ "date_range": {
197
+ "type": "object",
198
+ "description": "Date range for the export",
199
+ "properties": {
200
+ "start": {
201
+ "type": "string",
202
+ "format": "date"
203
+ },
204
+ "end": {
205
+ "type": "string",
206
+ "format": "date"
207
+ }
208
+ }
209
+ },
210
+ "columns": {
211
+ "type": "array",
212
+ "items": {
213
+ "type": "string"
214
+ },
215
+ "description": "Specific columns to include"
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ You can also fetch this live via the discovery endpoint:
222
+
223
+ ```bash
224
+ curl -s "https://aport.io/api/policies/data.export.create.v1?format=schema"
225
+ ```
226
+
@@ -0,0 +1,172 @@
1
+ const express = require("express");
2
+ const { requirePolicy } = require("@aporthq/middleware-express");
3
+
4
+ const app = express();
5
+ app.use(express.json());
6
+
7
+ // Apply data export policy to all export routes
8
+ app.post(
9
+ "/exports",
10
+ requirePolicy("data.export.create.v1"),
11
+ async (req, res) => {
12
+ try {
13
+ const { format, include_pii, filters } = req.body;
14
+ const passport = req.policyResult.passport;
15
+
16
+ // Check PII permission
17
+ if (include_pii && !passport.limits.allow_pii) {
18
+ return res.status(403).json({
19
+ error: "PII export not allowed",
20
+ agent_id: passport.agent_id,
21
+ upgrade_instructions:
22
+ "Request PII export capability from your administrator",
23
+ });
24
+ }
25
+
26
+ // Estimate row count (in real app, query your database)
27
+ const estimatedRows = await estimateExportRows(filters);
28
+
29
+ // Check row limit
30
+ if (estimatedRows > passport.limits.max_export_rows) {
31
+ return res.status(403).json({
32
+ error: "Export exceeds row limit",
33
+ requested: estimatedRows,
34
+ limit: passport.limits.max_export_rows,
35
+ upgrade_instructions: "Request smaller export or upgrade limits",
36
+ });
37
+ }
38
+
39
+ // Process export
40
+ const export_id = await createExport({
41
+ format,
42
+ include_pii,
43
+ filters,
44
+ agent_id: passport.agent_id,
45
+ agent_name: passport.name,
46
+ estimated_rows: estimatedRows,
47
+ });
48
+
49
+ // Log the export request
50
+ console.log(
51
+ `Export created: ${export_id} (${estimatedRows} rows) by agent ${passport.agent_id}`
52
+ );
53
+
54
+ res.json({
55
+ success: true,
56
+ export_id,
57
+ format,
58
+ estimated_rows: estimatedRows,
59
+ status: "processing",
60
+ });
61
+ } catch (error) {
62
+ console.error("Export creation error:", error);
63
+ res.status(500).json({ error: "Internal server error" });
64
+ }
65
+ }
66
+ );
67
+
68
+ // Get export status
69
+ app.get(
70
+ "/exports/:export_id",
71
+ requirePolicy("data.export.create.v1"),
72
+ async (req, res) => {
73
+ try {
74
+ const { export_id } = req.params;
75
+ const passport = req.policyResult.passport;
76
+
77
+ const export_info = await getExportStatus(export_id, passport.agent_id);
78
+
79
+ if (!export_info) {
80
+ return res.status(404).json({ error: "Export not found" });
81
+ }
82
+
83
+ res.json(export_info);
84
+ } catch (error) {
85
+ console.error("Export status error:", error);
86
+ res.status(500).json({ error: "Internal server error" });
87
+ }
88
+ }
89
+ );
90
+
91
+ // Download export file
92
+ app.get(
93
+ "/exports/:export_id/download",
94
+ requirePolicy("data.export.create.v1"),
95
+ async (req, res) => {
96
+ try {
97
+ const { export_id } = req.params;
98
+ const passport = req.policyResult.passport;
99
+
100
+ const export_file = await getExportFile(export_id, passport.agent_id);
101
+
102
+ if (!export_file) {
103
+ return res.status(404).json({ error: "Export file not found" });
104
+ }
105
+
106
+ if (export_file.status !== "completed") {
107
+ return res.status(400).json({ error: "Export not ready for download" });
108
+ }
109
+
110
+ // Set appropriate headers for file download
111
+ res.setHeader("Content-Type", export_file.content_type);
112
+ res.setHeader(
113
+ "Content-Disposition",
114
+ `attachment; filename="${export_file.filename}"`
115
+ );
116
+ res.send(export_file.data);
117
+ } catch (error) {
118
+ console.error("Export download error:", error);
119
+ res.status(500).json({ error: "Internal server error" });
120
+ }
121
+ }
122
+ );
123
+
124
+ // Mock functions
125
+ async function estimateExportRows(filters) {
126
+ // Simulate database query to estimate rows
127
+ await new Promise((resolve) => setTimeout(resolve, 50));
128
+ return Math.floor(Math.random() * 10000) + 1000;
129
+ }
130
+
131
+ async function createExport({
132
+ format,
133
+ include_pii,
134
+ filters,
135
+ agent_id,
136
+ estimated_rows,
137
+ }) {
138
+ // Simulate export creation
139
+ await new Promise((resolve) => setTimeout(resolve, 100));
140
+ return `exp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
141
+ }
142
+
143
+ async function getExportStatus(export_id, agent_id) {
144
+ // Simulate export status lookup
145
+ await new Promise((resolve) => setTimeout(resolve, 50));
146
+ return {
147
+ export_id,
148
+ status: "completed",
149
+ created_at: new Date().toISOString(),
150
+ estimated_rows: 5000,
151
+ actual_rows: 4876,
152
+ format: "csv",
153
+ include_pii: false,
154
+ };
155
+ }
156
+
157
+ async function getExportFile(export_id, agent_id) {
158
+ // Simulate file retrieval
159
+ await new Promise((resolve) => setTimeout(resolve, 50));
160
+ return {
161
+ data: "name,email,created_at\nJohn Doe,john@example.com,2024-01-01",
162
+ content_type: "text/csv",
163
+ filename: `export_${export_id}.csv`,
164
+ status: "completed",
165
+ };
166
+ }
167
+
168
+ const PORT = process.env.PORT || 3000;
169
+ app.listen(PORT, () => {
170
+ console.log(`Data export service running on port ${PORT}`);
171
+ console.log("Protected by APort data.export.create.v1 policy pack");
172
+ });