@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
|
@@ -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
|
+
[](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
|
+
});
|