@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,292 @@
|
|
|
1
|
+
# Data Access Governance Policy Pack v1
|
|
2
|
+
|
|
3
|
+
Protect your data access endpoints with APort's standardized policy pack. This pack ensures only verified agents with proper capabilities, assurance levels, and data access permissions can access sensitive data based on classification, entity types, and jurisdictional boundaries.
|
|
4
|
+
|
|
5
|
+
## What This Pack Protects
|
|
6
|
+
|
|
7
|
+
- **Route**: `/data/access/*` (GET, POST)
|
|
8
|
+
- **Risk**: Data breaches, privacy violations, regulatory compliance, unauthorized access
|
|
9
|
+
- **Impact**: Data exposure, GDPR violations, audit findings, reputational damage
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
| Requirement | Value | Description |
|
|
14
|
+
|-------------|-------|-------------|
|
|
15
|
+
| **Capability** | `data.access` | Agent must have data access capability |
|
|
16
|
+
| **Assurance** | `L3` or higher | Enhanced verification minimum |
|
|
17
|
+
| **Limits** | `allowed_classifications[]` | Allowed data classifications (PII, Financial, HR, ClientTier1) |
|
|
18
|
+
| **Limits** | `permissions.{classification}.allowed_entity_types[]` | Entity types allowed per classification |
|
|
19
|
+
| **Limits** | `permissions.{classification}.allowed_actions[]` | Actions allowed per classification |
|
|
20
|
+
| **Limits** | `allowed_jurisdictions[]` | Allowed data jurisdictions |
|
|
21
|
+
| **Limits** | `max_rows_per_export` | Maximum rows per data export |
|
|
22
|
+
| **Limits** | `allowed_destination_jurisdictions[]` | Allowed destination jurisdictions for data transfer |
|
|
23
|
+
| **Limits** | `balance_inquiry_cap_usd` | Maximum account balance for inquiry access |
|
|
24
|
+
| **Regions** | Must match | Agent must be authorized in caller's region |
|
|
25
|
+
|
|
26
|
+
## Implementation
|
|
27
|
+
|
|
28
|
+
### Express.js
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
const { requirePolicy } = require('@aporthq/middleware-express');
|
|
32
|
+
|
|
33
|
+
// Option 1: Explicit agent ID (preferred)
|
|
34
|
+
app.get('/data/access',
|
|
35
|
+
requirePolicy('governance.data.access.v1', 'ap_a2d10232c6534523812423eec8a1425c45678'),
|
|
36
|
+
async (req, res) => {
|
|
37
|
+
// Your data access logic here
|
|
38
|
+
// req.policyResult contains the verified passport
|
|
39
|
+
const { data_classification, accessing_entity_id, resource_id } = req.query;
|
|
40
|
+
const passport = req.policyResult.passport;
|
|
41
|
+
|
|
42
|
+
// Process data access...
|
|
43
|
+
res.json({ success: true, data: retrievedData });
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Option 2: Header fallback (backward compatible)
|
|
48
|
+
app.get('/data/access',
|
|
49
|
+
requirePolicy('governance.data.access.v1'),
|
|
50
|
+
async (req, res) => {
|
|
51
|
+
// Your data access logic here
|
|
52
|
+
// req.policyResult contains the verified passport
|
|
53
|
+
const { data_classification, accessing_entity_id, resource_id } = req.query;
|
|
54
|
+
const passport = req.policyResult.passport;
|
|
55
|
+
|
|
56
|
+
// Process data access...
|
|
57
|
+
res.json({ success: true, data: retrievedData });
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Client Request Example:**
|
|
63
|
+
```javascript
|
|
64
|
+
// The client must include the agent ID in the header (for Option 2)
|
|
65
|
+
fetch('/data/access?data_classification=PII&accessing_entity_id=emp_123&resource_id=user_456', {
|
|
66
|
+
method: 'GET',
|
|
67
|
+
headers: {
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
'X-Agent-Passport-Id': 'ap_a2d10232c6534523812423eec8a1425c45678' // ← Agent ID passed here
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### FastAPI
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from aport.middleware import require_policy
|
|
78
|
+
|
|
79
|
+
@app.get("/data/access")
|
|
80
|
+
@require_policy("governance.data.access.v1")
|
|
81
|
+
async def access_data(request: Request, data_classification: str, accessing_entity_id: str, resource_id: str):
|
|
82
|
+
# Your data access logic here
|
|
83
|
+
# request.state.policy_result contains the verified passport
|
|
84
|
+
passport = request.state.policy_result.passport
|
|
85
|
+
|
|
86
|
+
# Process data access...
|
|
87
|
+
return {"success": True, "data": retrieved_data}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Client Request Example:**
|
|
91
|
+
```python
|
|
92
|
+
import requests
|
|
93
|
+
|
|
94
|
+
# The client must include the agent ID in the header
|
|
95
|
+
response = requests.get('/data/access',
|
|
96
|
+
headers={
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
'X-Agent-Passport-Id': 'ap_a2d10232c6534523812423eec8a1425c45678' # ← Agent ID passed here
|
|
99
|
+
},
|
|
100
|
+
params={
|
|
101
|
+
'data_classification': 'PII',
|
|
102
|
+
'accessing_entity_id': 'emp_123',
|
|
103
|
+
'resource_id': 'user_456'
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## How It Works
|
|
109
|
+
|
|
110
|
+
The `requirePolicy('governance.data.access.v1', agentId?)` middleware implements a flexible approach:
|
|
111
|
+
|
|
112
|
+
1. **Agent ID Resolution** (in order of preference):
|
|
113
|
+
- **Function Parameter**: Uses explicit `agentId` if provided
|
|
114
|
+
- **Header Fallback**: Reads `X-Agent-Passport-Id` header from request
|
|
115
|
+
- **Validation**: Ensures agent ID format is valid (`ap_xxxxxxxxxxxxx`)
|
|
116
|
+
- **Failure**: Returns 400 error if neither provided
|
|
117
|
+
|
|
118
|
+
2. **Policy Validation**:
|
|
119
|
+
- **Format Check**: Validates policy ID format (`governance.data.access.v1`)
|
|
120
|
+
- **API Call**: Calls `/api/verify/policy/governance.data.access.v1` with agent ID and context
|
|
121
|
+
- **Requirements Check**: Validates agent meets governance.data.access.v1 requirements:
|
|
122
|
+
- Has `data.access` capability
|
|
123
|
+
- Meets minimum assurance level (L3+)
|
|
124
|
+
- Data classification is allowed
|
|
125
|
+
- Entity type is allowed for the classification
|
|
126
|
+
- Action type is allowed for the classification
|
|
127
|
+
- Jurisdiction is allowed
|
|
128
|
+
- Row count within limits (for exports)
|
|
129
|
+
- Destination jurisdiction allowed (for transfers)
|
|
130
|
+
- Balance inquiry within limits
|
|
131
|
+
- Access frequency within limits
|
|
132
|
+
- Data retention policy compliance
|
|
133
|
+
|
|
134
|
+
3. **Result Handling**:
|
|
135
|
+
- **Success**: Adds `req.policyResult` with verified data and continues
|
|
136
|
+
- **Failure**: Returns 403 with detailed error information
|
|
137
|
+
- **Logging**: Logs violations for monitoring and debugging
|
|
138
|
+
|
|
139
|
+
## Error Responses
|
|
140
|
+
|
|
141
|
+
When policy checks fail, you'll receive a `403 Forbidden` with detailed error information:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"error": "policy_violation",
|
|
146
|
+
"code": "oap.classification_forbidden",
|
|
147
|
+
"message": "Data classification 'Sensitive' is not allowed",
|
|
148
|
+
"policy_id": "governance.data.access.v1",
|
|
149
|
+
"agent_id": "ap_a2d10232c6534523812423eec8a1425c45678",
|
|
150
|
+
"upgrade_instructions": "Request access to this data classification in your passport"
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Test Payloads
|
|
155
|
+
|
|
156
|
+
### Valid Request
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"data_classification": "PII",
|
|
160
|
+
"accessing_entity_id": "emp_123",
|
|
161
|
+
"accessing_entity_type": "employee",
|
|
162
|
+
"resource_id": "user_456",
|
|
163
|
+
"action_type": "read",
|
|
164
|
+
"jurisdiction": "US",
|
|
165
|
+
"row_count": 100
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Invalid Request (classification forbidden)
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"data_classification": "Sensitive",
|
|
173
|
+
"accessing_entity_id": "emp_123",
|
|
174
|
+
"accessing_entity_type": "employee",
|
|
175
|
+
"resource_id": "user_456"
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
*Returns 403: "Data classification 'Sensitive' is not allowed"*
|
|
179
|
+
|
|
180
|
+
## Best Practices
|
|
181
|
+
|
|
182
|
+
1. **Cache Verification**: Cache `/verify` responses with ETag for 60 seconds
|
|
183
|
+
2. **Webhook Integration**: Subscribe to `status.changed` webhooks for instant suspension
|
|
184
|
+
3. **Verifiable Attestation**: Log all data access attempts for compliance
|
|
185
|
+
4. **Data Classification**: Implement data classification tagging for all resources
|
|
186
|
+
5. **Entity Access Matrices**: Maintain entity access matrices for different data types
|
|
187
|
+
6. **Jurisdiction Awareness**: Use jurisdiction-aware data routing for compliance
|
|
188
|
+
7. **Progressive Disclosure**: Implement progressive disclosure for sensitive data access
|
|
189
|
+
8. **Access Monitoring**: Monitor access patterns for unusual behavior
|
|
190
|
+
9. **Audit Trails**: Maintain audit trails for all data access operations
|
|
191
|
+
10. **Encryption**: Use encryption for data in transit and at rest
|
|
192
|
+
11. **Data Retention**: Implement data retention policies based on classification
|
|
193
|
+
|
|
194
|
+
## Compliance Badge
|
|
195
|
+
|
|
196
|
+
Agents that meet this policy's requirements can display the "Data-Access-Ready" badge:
|
|
197
|
+
|
|
198
|
+
```markdown
|
|
199
|
+
[](https://aport.io/agents/ap_a2d10232c6534523812423eec8a1425c45678)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Support
|
|
203
|
+
|
|
204
|
+
- [Documentation](https://aport.io/docs/policies/governance.data.access.v1)
|
|
205
|
+
- [Community](https://github.com/aporthq/community)
|
|
206
|
+
- [Support](https://aport.io/support)
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
**Last Updated**: 2025-01-30 00:00:00 UTC
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
## Required Context
|
|
213
|
+
|
|
214
|
+
This policy requires the following context (JSON Schema):
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
219
|
+
"type": "object",
|
|
220
|
+
"required": [
|
|
221
|
+
"data_classification",
|
|
222
|
+
"accessing_entity_id",
|
|
223
|
+
"accessing_entity_type",
|
|
224
|
+
"resource_id"
|
|
225
|
+
],
|
|
226
|
+
"properties": {
|
|
227
|
+
"data_classification": {
|
|
228
|
+
"type": "string",
|
|
229
|
+
"description": "The classification of the data being accessed (e.g., 'PII', 'Financial', 'HR', 'ClientTier1')."
|
|
230
|
+
},
|
|
231
|
+
"accessing_entity_id": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"description": "The unique ID of the entity (user, agent, employee) attempting the access."
|
|
234
|
+
},
|
|
235
|
+
"accessing_entity_type": {
|
|
236
|
+
"type": "string",
|
|
237
|
+
"enum": [
|
|
238
|
+
"employee",
|
|
239
|
+
"client",
|
|
240
|
+
"system_agent"
|
|
241
|
+
],
|
|
242
|
+
"description": "The type of entity attempting the access."
|
|
243
|
+
},
|
|
244
|
+
"resource_id": {
|
|
245
|
+
"type": "string",
|
|
246
|
+
"description": "The unique ID of the data resource being accessed (e.g., account number, user profile ID)."
|
|
247
|
+
},
|
|
248
|
+
"action_type": {
|
|
249
|
+
"type": "string",
|
|
250
|
+
"enum": [
|
|
251
|
+
"read",
|
|
252
|
+
"export",
|
|
253
|
+
"delete",
|
|
254
|
+
"update"
|
|
255
|
+
],
|
|
256
|
+
"default": "read",
|
|
257
|
+
"description": "The type of data access action being performed."
|
|
258
|
+
},
|
|
259
|
+
"jurisdiction": {
|
|
260
|
+
"type": "string",
|
|
261
|
+
"pattern": "^[A-Z]{2}$",
|
|
262
|
+
"description": "The ISO 3166-1 alpha-2 country code relevant to the data's jurisdiction."
|
|
263
|
+
},
|
|
264
|
+
"row_count": {
|
|
265
|
+
"type": "integer",
|
|
266
|
+
"description": "The number of rows/records being accessed or exported. Solves for Row Limits."
|
|
267
|
+
},
|
|
268
|
+
"destination_jurisdiction": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"pattern": "^[A-Z]{2}$",
|
|
271
|
+
"description": "The destination country code for data transfers. Solves for Data Locality."
|
|
272
|
+
},
|
|
273
|
+
"resource_attributes": {
|
|
274
|
+
"type": "object",
|
|
275
|
+
"description": "A flexible object for passing specific attributes of the resource being accessed. Solves for Balance Inquiry.",
|
|
276
|
+
"properties": {
|
|
277
|
+
"account_balance_usd": {
|
|
278
|
+
"type": "integer",
|
|
279
|
+
"description": "The balance of the account in USD minor units, used for balance-based access rules."
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
You can also fetch this live via the discovery endpoint:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
curl -s "https://aport.io/api/policies/governance.data.access.v1?format=schema"
|
|
291
|
+
```
|
|
292
|
+
|
|
@@ -0,0 +1,321 @@
|
|
|
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 governance.data.access policy to all data access routes
|
|
8
|
+
app.get(
|
|
9
|
+
"/data/access",
|
|
10
|
+
requirePolicy("governance.data.access.v1"),
|
|
11
|
+
async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const {
|
|
14
|
+
data_classification,
|
|
15
|
+
accessing_entity_id,
|
|
16
|
+
accessing_entity_type,
|
|
17
|
+
resource_id,
|
|
18
|
+
action_type,
|
|
19
|
+
jurisdiction,
|
|
20
|
+
row_count,
|
|
21
|
+
destination_jurisdiction,
|
|
22
|
+
resource_attributes,
|
|
23
|
+
} = req.query;
|
|
24
|
+
|
|
25
|
+
const passport = req.policyResult.passport;
|
|
26
|
+
|
|
27
|
+
// Additional business logic validation
|
|
28
|
+
if (!data_classification || !accessing_entity_id || !resource_id) {
|
|
29
|
+
return res.status(400).json({ error: "Missing required parameters" });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Process data access using your data system
|
|
33
|
+
const access_id = await processDataAccess({
|
|
34
|
+
data_classification,
|
|
35
|
+
accessing_entity_id,
|
|
36
|
+
accessing_entity_type,
|
|
37
|
+
resource_id,
|
|
38
|
+
action_type: action_type || "read",
|
|
39
|
+
jurisdiction,
|
|
40
|
+
row_count: row_count ? parseInt(row_count) : undefined,
|
|
41
|
+
destination_jurisdiction,
|
|
42
|
+
resource_attributes: resource_attributes
|
|
43
|
+
? JSON.parse(resource_attributes)
|
|
44
|
+
: undefined,
|
|
45
|
+
agent_id: passport.passport_id,
|
|
46
|
+
agent_name: passport.metadata?.template_name || "Unknown Agent",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Log the data access
|
|
50
|
+
console.log(
|
|
51
|
+
`Data access processed: ${access_id} for ${data_classification} data by agent ${passport.passport_id}`
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
res.json({
|
|
55
|
+
success: true,
|
|
56
|
+
access_id,
|
|
57
|
+
data_classification,
|
|
58
|
+
resource_id,
|
|
59
|
+
action_type: action_type || "read",
|
|
60
|
+
status: "processed",
|
|
61
|
+
decision_id: req.policyResult.decision_id,
|
|
62
|
+
});
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("Data access processing error:", error);
|
|
65
|
+
res.status(500).json({ error: "Internal server error" });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Data export endpoint
|
|
71
|
+
app.post(
|
|
72
|
+
"/data/export",
|
|
73
|
+
requirePolicy("governance.data.access.v1"),
|
|
74
|
+
async (req, res) => {
|
|
75
|
+
try {
|
|
76
|
+
const {
|
|
77
|
+
data_classification,
|
|
78
|
+
accessing_entity_id,
|
|
79
|
+
accessing_entity_type,
|
|
80
|
+
resource_id,
|
|
81
|
+
action_type,
|
|
82
|
+
jurisdiction,
|
|
83
|
+
row_count,
|
|
84
|
+
destination_jurisdiction,
|
|
85
|
+
export_format,
|
|
86
|
+
filters,
|
|
87
|
+
} = req.body;
|
|
88
|
+
|
|
89
|
+
const passport = req.policyResult.passport;
|
|
90
|
+
|
|
91
|
+
// Check row count limits
|
|
92
|
+
const maxRowsPerExport =
|
|
93
|
+
passport.limits?.data?.access?.max_rows_per_export;
|
|
94
|
+
if (maxRowsPerExport && row_count > maxRowsPerExport) {
|
|
95
|
+
return res.status(403).json({
|
|
96
|
+
error: "Export row count exceeds limit",
|
|
97
|
+
row_count,
|
|
98
|
+
limit: maxRowsPerExport,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Process data export
|
|
103
|
+
const export_id = await processDataExport({
|
|
104
|
+
data_classification,
|
|
105
|
+
accessing_entity_id,
|
|
106
|
+
accessing_entity_type,
|
|
107
|
+
resource_id,
|
|
108
|
+
action_type: action_type || "export",
|
|
109
|
+
jurisdiction,
|
|
110
|
+
row_count,
|
|
111
|
+
destination_jurisdiction,
|
|
112
|
+
export_format,
|
|
113
|
+
filters,
|
|
114
|
+
agent_id: passport.passport_id,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
res.json({
|
|
118
|
+
success: true,
|
|
119
|
+
export_id,
|
|
120
|
+
data_classification,
|
|
121
|
+
row_count,
|
|
122
|
+
export_format,
|
|
123
|
+
status: "exported",
|
|
124
|
+
decision_id: req.policyResult.decision_id,
|
|
125
|
+
});
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error("Data export error:", error);
|
|
128
|
+
res.status(500).json({ error: "Internal server error" });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Balance inquiry endpoint
|
|
134
|
+
app.get(
|
|
135
|
+
"/data/balance/:account_id",
|
|
136
|
+
requirePolicy("governance.data.access.v1"),
|
|
137
|
+
async (req, res) => {
|
|
138
|
+
try {
|
|
139
|
+
const { account_id } = req.params;
|
|
140
|
+
const { accessing_entity_id, accessing_entity_type } = req.query;
|
|
141
|
+
const passport = req.policyResult.passport;
|
|
142
|
+
|
|
143
|
+
// Get account balance
|
|
144
|
+
const balance_info = await getAccountBalance(
|
|
145
|
+
account_id,
|
|
146
|
+
accessing_entity_id,
|
|
147
|
+
passport.passport_id
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (!balance_info) {
|
|
151
|
+
return res.status(404).json({ error: "Account not found" });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check balance inquiry limits
|
|
155
|
+
const balanceInquiryCap =
|
|
156
|
+
passport.limits?.data?.access?.balance_inquiry_cap_usd;
|
|
157
|
+
if (balanceInquiryCap && balance_info.balance_usd >= balanceInquiryCap) {
|
|
158
|
+
return res.status(403).json({
|
|
159
|
+
error: "Account balance exceeds inquiry cap",
|
|
160
|
+
balance: balance_info.balance_usd,
|
|
161
|
+
cap: balanceInquiryCap,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
res.json({
|
|
166
|
+
success: true,
|
|
167
|
+
account_id,
|
|
168
|
+
balance_usd: balance_info.balance_usd,
|
|
169
|
+
currency: balance_info.currency,
|
|
170
|
+
last_updated: balance_info.last_updated,
|
|
171
|
+
decision_id: req.policyResult.decision_id,
|
|
172
|
+
});
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error("Balance inquiry error:", error);
|
|
175
|
+
res.status(500).json({ error: "Internal server error" });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
// Data access audit endpoint
|
|
181
|
+
app.get(
|
|
182
|
+
"/data/access/audit",
|
|
183
|
+
requirePolicy("governance.data.access.v1"),
|
|
184
|
+
async (req, res) => {
|
|
185
|
+
try {
|
|
186
|
+
const { start_date, end_date, data_classification } = req.query;
|
|
187
|
+
const passport = req.policyResult.passport;
|
|
188
|
+
|
|
189
|
+
const audit_logs = await getDataAccessAuditLogs({
|
|
190
|
+
start_date,
|
|
191
|
+
end_date,
|
|
192
|
+
data_classification,
|
|
193
|
+
agent_id: passport.passport_id,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
res.json({
|
|
197
|
+
success: true,
|
|
198
|
+
audit_logs,
|
|
199
|
+
total_entries: audit_logs.length,
|
|
200
|
+
decision_id: req.policyResult.decision_id,
|
|
201
|
+
});
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error("Audit log error:", error);
|
|
204
|
+
res.status(500).json({ error: "Internal server error" });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// Mock data access processing function
|
|
210
|
+
async function processDataAccess({
|
|
211
|
+
data_classification,
|
|
212
|
+
accessing_entity_id,
|
|
213
|
+
accessing_entity_type,
|
|
214
|
+
resource_id,
|
|
215
|
+
action_type,
|
|
216
|
+
jurisdiction,
|
|
217
|
+
row_count,
|
|
218
|
+
destination_jurisdiction,
|
|
219
|
+
resource_attributes,
|
|
220
|
+
agent_id,
|
|
221
|
+
}) {
|
|
222
|
+
// Simulate data system call
|
|
223
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
224
|
+
|
|
225
|
+
// Log data access details for audit
|
|
226
|
+
console.log(`Processing data access:`, {
|
|
227
|
+
data_classification,
|
|
228
|
+
accessing_entity_id,
|
|
229
|
+
accessing_entity_type,
|
|
230
|
+
resource_id,
|
|
231
|
+
action_type,
|
|
232
|
+
jurisdiction,
|
|
233
|
+
row_count,
|
|
234
|
+
destination_jurisdiction,
|
|
235
|
+
resource_attributes,
|
|
236
|
+
agent_id,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return `access_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Mock data export processing function
|
|
243
|
+
async function processDataExport({
|
|
244
|
+
data_classification,
|
|
245
|
+
accessing_entity_id,
|
|
246
|
+
accessing_entity_type,
|
|
247
|
+
resource_id,
|
|
248
|
+
action_type,
|
|
249
|
+
jurisdiction,
|
|
250
|
+
row_count,
|
|
251
|
+
destination_jurisdiction,
|
|
252
|
+
export_format,
|
|
253
|
+
filters,
|
|
254
|
+
agent_id,
|
|
255
|
+
}) {
|
|
256
|
+
// Simulate data export processing
|
|
257
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
258
|
+
|
|
259
|
+
console.log(`Processing data export:`, {
|
|
260
|
+
data_classification,
|
|
261
|
+
accessing_entity_id,
|
|
262
|
+
accessing_entity_type,
|
|
263
|
+
resource_id,
|
|
264
|
+
action_type,
|
|
265
|
+
jurisdiction,
|
|
266
|
+
row_count,
|
|
267
|
+
destination_jurisdiction,
|
|
268
|
+
export_format,
|
|
269
|
+
filters,
|
|
270
|
+
agent_id,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return `export_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Mock account balance lookup
|
|
277
|
+
async function getAccountBalance(account_id, accessing_entity_id, agent_id) {
|
|
278
|
+
// Simulate account balance lookup
|
|
279
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
280
|
+
return {
|
|
281
|
+
account_id,
|
|
282
|
+
balance_usd: 50000, // $500.00 in cents
|
|
283
|
+
currency: "USD",
|
|
284
|
+
last_updated: new Date().toISOString(),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Mock audit log lookup
|
|
289
|
+
async function getDataAccessAuditLogs({
|
|
290
|
+
start_date,
|
|
291
|
+
end_date,
|
|
292
|
+
data_classification,
|
|
293
|
+
agent_id,
|
|
294
|
+
}) {
|
|
295
|
+
// Simulate audit log lookup
|
|
296
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
297
|
+
return [
|
|
298
|
+
{
|
|
299
|
+
access_id: "access_123",
|
|
300
|
+
data_classification: "PII",
|
|
301
|
+
resource_id: "user_456",
|
|
302
|
+
action_type: "read",
|
|
303
|
+
timestamp: new Date().toISOString(),
|
|
304
|
+
agent_id,
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
access_id: "access_124",
|
|
308
|
+
data_classification: "Financial",
|
|
309
|
+
resource_id: "account_789",
|
|
310
|
+
action_type: "export",
|
|
311
|
+
timestamp: new Date().toISOString(),
|
|
312
|
+
agent_id,
|
|
313
|
+
},
|
|
314
|
+
];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const PORT = process.env.PORT || 3000;
|
|
318
|
+
app.listen(PORT, () => {
|
|
319
|
+
console.log(`Data access governance service running on port ${PORT}`);
|
|
320
|
+
console.log("Protected by APort governance.data.access.v1 policy pack");
|
|
321
|
+
});
|