@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,279 @@
|
|
|
1
|
+
from fastapi import FastAPI, HTTPException, Request, Query
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from aport.middleware import require_policy
|
|
4
|
+
import asyncio
|
|
5
|
+
from typing import List, Optional, Dict, Any
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
app = FastAPI(title="Data Access Governance Service", version="1.0.0")
|
|
9
|
+
|
|
10
|
+
class DataExportRequest(BaseModel):
|
|
11
|
+
data_classification: str
|
|
12
|
+
accessing_entity_id: str
|
|
13
|
+
accessing_entity_type: str
|
|
14
|
+
resource_id: str
|
|
15
|
+
action_type: Optional[str] = "export"
|
|
16
|
+
jurisdiction: Optional[str] = None
|
|
17
|
+
row_count: int
|
|
18
|
+
destination_jurisdiction: Optional[str] = None
|
|
19
|
+
export_format: str = "json"
|
|
20
|
+
filters: Optional[Dict[str, Any]] = None
|
|
21
|
+
|
|
22
|
+
class DataAccessResponse(BaseModel):
|
|
23
|
+
success: bool
|
|
24
|
+
access_id: str
|
|
25
|
+
data_classification: str
|
|
26
|
+
resource_id: str
|
|
27
|
+
action_type: str
|
|
28
|
+
status: str
|
|
29
|
+
decision_id: str
|
|
30
|
+
|
|
31
|
+
class DataExportResponse(BaseModel):
|
|
32
|
+
success: bool
|
|
33
|
+
export_id: str
|
|
34
|
+
data_classification: str
|
|
35
|
+
row_count: int
|
|
36
|
+
export_format: str
|
|
37
|
+
status: str
|
|
38
|
+
decision_id: str
|
|
39
|
+
|
|
40
|
+
class BalanceInfo(BaseModel):
|
|
41
|
+
account_id: str
|
|
42
|
+
balance_usd: int
|
|
43
|
+
currency: str
|
|
44
|
+
last_updated: str
|
|
45
|
+
|
|
46
|
+
class AuditLog(BaseModel):
|
|
47
|
+
access_id: str
|
|
48
|
+
data_classification: str
|
|
49
|
+
resource_id: str
|
|
50
|
+
action_type: str
|
|
51
|
+
timestamp: str
|
|
52
|
+
agent_id: str
|
|
53
|
+
|
|
54
|
+
@app.get("/data/access")
|
|
55
|
+
@require_policy("governance.data.access.v1")
|
|
56
|
+
async def access_data(
|
|
57
|
+
request: Request,
|
|
58
|
+
data_classification: str = Query(...),
|
|
59
|
+
accessing_entity_id: str = Query(...),
|
|
60
|
+
accessing_entity_type: str = Query(...),
|
|
61
|
+
resource_id: str = Query(...),
|
|
62
|
+
action_type: str = Query("read"),
|
|
63
|
+
jurisdiction: Optional[str] = Query(None),
|
|
64
|
+
row_count: Optional[int] = Query(None),
|
|
65
|
+
destination_jurisdiction: Optional[str] = Query(None),
|
|
66
|
+
resource_attributes: Optional[str] = Query(None)
|
|
67
|
+
):
|
|
68
|
+
try:
|
|
69
|
+
passport = request.state.policy_result.passport
|
|
70
|
+
|
|
71
|
+
# Additional business logic validation
|
|
72
|
+
if not data_classification or not accessing_entity_id or not resource_id:
|
|
73
|
+
raise HTTPException(status_code=400, detail="Missing required parameters")
|
|
74
|
+
|
|
75
|
+
# Process data access using your data system
|
|
76
|
+
access_id = await process_data_access({
|
|
77
|
+
"data_classification": data_classification,
|
|
78
|
+
"accessing_entity_id": accessing_entity_id,
|
|
79
|
+
"accessing_entity_type": accessing_entity_type,
|
|
80
|
+
"resource_id": resource_id,
|
|
81
|
+
"action_type": action_type,
|
|
82
|
+
"jurisdiction": jurisdiction,
|
|
83
|
+
"row_count": row_count,
|
|
84
|
+
"destination_jurisdiction": destination_jurisdiction,
|
|
85
|
+
"resource_attributes": json.loads(resource_attributes) if resource_attributes else None,
|
|
86
|
+
"agent_id": passport.passport_id,
|
|
87
|
+
"agent_name": passport.metadata.get("template_name", "Unknown Agent") if passport.metadata else "Unknown Agent"
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
# Log the data access
|
|
91
|
+
print(f"Data access processed: {access_id} for {data_classification} data by agent {passport.passport_id}")
|
|
92
|
+
|
|
93
|
+
return DataAccessResponse(
|
|
94
|
+
success=True,
|
|
95
|
+
access_id=access_id,
|
|
96
|
+
data_classification=data_classification,
|
|
97
|
+
resource_id=resource_id,
|
|
98
|
+
action_type=action_type,
|
|
99
|
+
status="processed",
|
|
100
|
+
decision_id=request.state.policy_result.decision_id
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f"Data access processing error: {e}")
|
|
105
|
+
raise HTTPException(status_code=500, detail="Internal server error")
|
|
106
|
+
|
|
107
|
+
@app.post("/data/export")
|
|
108
|
+
@require_policy("governance.data.access.v1")
|
|
109
|
+
async def export_data(request: Request, export_data: DataExportRequest):
|
|
110
|
+
try:
|
|
111
|
+
passport = request.state.policy_result.passport
|
|
112
|
+
|
|
113
|
+
# Check row count limits
|
|
114
|
+
max_rows_per_export = passport.limits.get("data", {}).get("access", {}).get("max_rows_per_export")
|
|
115
|
+
if max_rows_per_export and export_data.row_count > max_rows_per_export:
|
|
116
|
+
raise HTTPException(
|
|
117
|
+
status_code=403,
|
|
118
|
+
detail={
|
|
119
|
+
"error": "Export row count exceeds limit",
|
|
120
|
+
"row_count": export_data.row_count,
|
|
121
|
+
"limit": max_rows_per_export
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Process data export
|
|
126
|
+
export_id = await process_data_export({
|
|
127
|
+
"data_classification": export_data.data_classification,
|
|
128
|
+
"accessing_entity_id": export_data.accessing_entity_id,
|
|
129
|
+
"accessing_entity_type": export_data.accessing_entity_type,
|
|
130
|
+
"resource_id": export_data.resource_id,
|
|
131
|
+
"action_type": export_data.action_type,
|
|
132
|
+
"jurisdiction": export_data.jurisdiction,
|
|
133
|
+
"row_count": export_data.row_count,
|
|
134
|
+
"destination_jurisdiction": export_data.destination_jurisdiction,
|
|
135
|
+
"export_format": export_data.export_format,
|
|
136
|
+
"filters": export_data.filters,
|
|
137
|
+
"agent_id": passport.passport_id
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
return DataExportResponse(
|
|
141
|
+
success=True,
|
|
142
|
+
export_id=export_id,
|
|
143
|
+
data_classification=export_data.data_classification,
|
|
144
|
+
row_count=export_data.row_count,
|
|
145
|
+
export_format=export_data.export_format,
|
|
146
|
+
status="exported",
|
|
147
|
+
decision_id=request.state.policy_result.decision_id
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
except Exception as e:
|
|
151
|
+
print(f"Data export error: {e}")
|
|
152
|
+
raise HTTPException(status_code=500, detail="Internal server error")
|
|
153
|
+
|
|
154
|
+
@app.get("/data/balance/{account_id}")
|
|
155
|
+
@require_policy("governance.data.access.v1")
|
|
156
|
+
async def get_balance(
|
|
157
|
+
request: Request,
|
|
158
|
+
account_id: str,
|
|
159
|
+
accessing_entity_id: str = Query(...),
|
|
160
|
+
accessing_entity_type: str = Query(...)
|
|
161
|
+
):
|
|
162
|
+
try:
|
|
163
|
+
passport = request.state.policy_result.passport
|
|
164
|
+
|
|
165
|
+
# Get account balance
|
|
166
|
+
balance_info = await get_account_balance(account_id, accessing_entity_id, passport.passport_id)
|
|
167
|
+
|
|
168
|
+
if not balance_info:
|
|
169
|
+
raise HTTPException(status_code=404, detail="Account not found")
|
|
170
|
+
|
|
171
|
+
# Check balance inquiry limits
|
|
172
|
+
balance_inquiry_cap = passport.limits.get("data", {}).get("access", {}).get("balance_inquiry_cap_usd")
|
|
173
|
+
if balance_inquiry_cap and balance_info["balance_usd"] >= balance_inquiry_cap:
|
|
174
|
+
raise HTTPException(
|
|
175
|
+
status_code=403,
|
|
176
|
+
detail={
|
|
177
|
+
"error": "Account balance exceeds inquiry cap",
|
|
178
|
+
"balance": balance_info["balance_usd"],
|
|
179
|
+
"cap": balance_inquiry_cap
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return BalanceInfo(
|
|
184
|
+
account_id=account_id,
|
|
185
|
+
balance_usd=balance_info["balance_usd"],
|
|
186
|
+
currency=balance_info["currency"],
|
|
187
|
+
last_updated=balance_info["last_updated"]
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
except Exception as e:
|
|
191
|
+
print(f"Balance inquiry error: {e}")
|
|
192
|
+
raise HTTPException(status_code=500, detail="Internal server error")
|
|
193
|
+
|
|
194
|
+
@app.get("/data/access/audit")
|
|
195
|
+
@require_policy("governance.data.access.v1")
|
|
196
|
+
async def get_audit_logs(
|
|
197
|
+
request: Request,
|
|
198
|
+
start_date: Optional[str] = Query(None),
|
|
199
|
+
end_date: Optional[str] = Query(None),
|
|
200
|
+
data_classification: Optional[str] = Query(None)
|
|
201
|
+
):
|
|
202
|
+
try:
|
|
203
|
+
passport = request.state.policy_result.passport
|
|
204
|
+
|
|
205
|
+
audit_logs = await get_data_access_audit_logs({
|
|
206
|
+
"start_date": start_date,
|
|
207
|
+
"end_date": end_date,
|
|
208
|
+
"data_classification": data_classification,
|
|
209
|
+
"agent_id": passport.passport_id
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
"success": True,
|
|
214
|
+
"audit_logs": audit_logs,
|
|
215
|
+
"total_entries": len(audit_logs),
|
|
216
|
+
"decision_id": request.state.policy_result.decision_id
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
print(f"Audit log error: {e}")
|
|
221
|
+
raise HTTPException(status_code=500, detail="Internal server error")
|
|
222
|
+
|
|
223
|
+
async def process_data_access(data_access_data: dict) -> str:
|
|
224
|
+
"""Mock data access processing function"""
|
|
225
|
+
# Simulate data system call
|
|
226
|
+
await asyncio.sleep(0.1)
|
|
227
|
+
|
|
228
|
+
# Log data access details for audit
|
|
229
|
+
print(f"Processing data access: {data_access_data}")
|
|
230
|
+
|
|
231
|
+
return f"access_{asyncio.get_event_loop().time()}_{hash(str(data_access_data)) % 1000000}"
|
|
232
|
+
|
|
233
|
+
async def process_data_export(export_data: dict) -> str:
|
|
234
|
+
"""Mock data export processing function"""
|
|
235
|
+
# Simulate data export processing
|
|
236
|
+
await asyncio.sleep(0.2)
|
|
237
|
+
|
|
238
|
+
print(f"Processing data export: {export_data}")
|
|
239
|
+
|
|
240
|
+
return f"export_{asyncio.get_event_loop().time()}_{hash(str(export_data)) % 1000000}"
|
|
241
|
+
|
|
242
|
+
async def get_account_balance(account_id: str, accessing_entity_id: str, agent_id: str) -> Optional[Dict[str, Any]]:
|
|
243
|
+
"""Mock account balance lookup"""
|
|
244
|
+
await asyncio.sleep(0.05)
|
|
245
|
+
return {
|
|
246
|
+
"account_id": account_id,
|
|
247
|
+
"balance_usd": 50000, # $500.00 in cents
|
|
248
|
+
"currency": "USD",
|
|
249
|
+
"last_updated": datetime.now().isoformat()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async def get_data_access_audit_logs(params: dict) -> List[Dict[str, Any]]:
|
|
253
|
+
"""Mock audit log lookup"""
|
|
254
|
+
await asyncio.sleep(0.1)
|
|
255
|
+
return [
|
|
256
|
+
{
|
|
257
|
+
"access_id": "access_123",
|
|
258
|
+
"data_classification": "PII",
|
|
259
|
+
"resource_id": "user_456",
|
|
260
|
+
"action_type": "read",
|
|
261
|
+
"timestamp": datetime.now().isoformat(),
|
|
262
|
+
"agent_id": params["agent_id"]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"access_id": "access_124",
|
|
266
|
+
"data_classification": "Financial",
|
|
267
|
+
"resource_id": "account_789",
|
|
268
|
+
"action_type": "export",
|
|
269
|
+
"timestamp": datetime.now().isoformat(),
|
|
270
|
+
"agent_id": params["agent_id"]
|
|
271
|
+
}
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
if __name__ == "__main__":
|
|
275
|
+
import uvicorn
|
|
276
|
+
import json
|
|
277
|
+
print("Data access governance service starting...")
|
|
278
|
+
print("Protected by APort governance.data.access.v1 policy pack")
|
|
279
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Example: governance.data.access.v1 Policy
|
|
3
|
+
*
|
|
4
|
+
* This is a quick-start example showing the basic usage of the governance.data.access.v1 policy.
|
|
5
|
+
* For production use, see the full express.example.js file.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const express = require("express");
|
|
9
|
+
const { requirePolicy } = require("@aporthq/middleware-express");
|
|
10
|
+
|
|
11
|
+
const app = express();
|
|
12
|
+
app.use(express.json());
|
|
13
|
+
|
|
14
|
+
// Minimal data access endpoint with policy protection
|
|
15
|
+
app.get(
|
|
16
|
+
"/data/access",
|
|
17
|
+
requirePolicy("governance.data.access.v1"),
|
|
18
|
+
async (req, res) => {
|
|
19
|
+
try {
|
|
20
|
+
const { data_classification, accessing_entity_id, resource_id } =
|
|
21
|
+
req.query;
|
|
22
|
+
const passport = req.policyResult.passport;
|
|
23
|
+
|
|
24
|
+
// Process the data access (your business logic here)
|
|
25
|
+
const access_id = `access_${Date.now()}`;
|
|
26
|
+
|
|
27
|
+
console.log(
|
|
28
|
+
`Data access processed: ${access_id} for ${data_classification} data`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
res.json({
|
|
32
|
+
success: true,
|
|
33
|
+
access_id,
|
|
34
|
+
data_classification,
|
|
35
|
+
resource_id,
|
|
36
|
+
decision_id: req.policyResult.decision_id,
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error("Data access error:", error);
|
|
40
|
+
res.status(500).json({ error: "Internal server error" });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Example client request
|
|
46
|
+
const exampleRequest = {
|
|
47
|
+
data_classification: "PII",
|
|
48
|
+
accessing_entity_id: "emp_123",
|
|
49
|
+
accessing_entity_type: "employee",
|
|
50
|
+
resource_id: "user_456",
|
|
51
|
+
action_type: "read",
|
|
52
|
+
jurisdiction: "US",
|
|
53
|
+
row_count: 100,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
console.log("Example request:", JSON.stringify(exampleRequest, null, 2));
|
|
57
|
+
|
|
58
|
+
const PORT = process.env.PORT || 3000;
|
|
59
|
+
app.listen(PORT, () => {
|
|
60
|
+
console.log(`Minimal data access service running on port ${PORT}`);
|
|
61
|
+
console.log("Protected by APort governance.data.access.v1 policy");
|
|
62
|
+
console.log(
|
|
63
|
+
`Try: curl -X GET "http://localhost:${PORT}/data/access?data_classification=PII&accessing_entity_id=emp_123&resource_id=user_456"`
|
|
64
|
+
);
|
|
65
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "governance.data.access.v1",
|
|
3
|
+
"name": "Data Access Governance Policy",
|
|
4
|
+
"description": "Pre-action governance for agent-initiated data access. Enforces controls based on data classification, sensitivity, entity access rights, and jurisdictional boundaries.",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "active",
|
|
7
|
+
"requires_capabilities": ["data.access"],
|
|
8
|
+
"min_assurance": "L3",
|
|
9
|
+
"limits_required": [
|
|
10
|
+
"allowed_classifications",
|
|
11
|
+
"permissions",
|
|
12
|
+
"allowed_jurisdictions",
|
|
13
|
+
"max_rows_per_export",
|
|
14
|
+
"allowed_destination_jurisdictions",
|
|
15
|
+
"balance_inquiry_cap_usd"
|
|
16
|
+
],
|
|
17
|
+
"required_fields": [
|
|
18
|
+
"data_classification",
|
|
19
|
+
"accessing_entity_id",
|
|
20
|
+
"accessing_entity_type",
|
|
21
|
+
"resource_id"
|
|
22
|
+
],
|
|
23
|
+
"optional_fields": [
|
|
24
|
+
"action_type",
|
|
25
|
+
"jurisdiction",
|
|
26
|
+
"row_count",
|
|
27
|
+
"destination_jurisdiction",
|
|
28
|
+
"resource_attributes"
|
|
29
|
+
],
|
|
30
|
+
"enforcement": {
|
|
31
|
+
"classification_allowed": true,
|
|
32
|
+
"entity_type_allowed": true,
|
|
33
|
+
"jurisdiction_check": true,
|
|
34
|
+
"row_limit_enforced": true,
|
|
35
|
+
"data_locality_enforced": true,
|
|
36
|
+
"balance_inquiry_limited": true
|
|
37
|
+
},
|
|
38
|
+
"mcp": {
|
|
39
|
+
"require_allowlisted_if_present": true
|
|
40
|
+
},
|
|
41
|
+
"advice": [
|
|
42
|
+
"Cache /verify with ETag; 60s TTL",
|
|
43
|
+
"Subscribe to status webhooks for instant suspend",
|
|
44
|
+
"Log all data access attempts for Verifiable Attestation",
|
|
45
|
+
"Implement data classification tagging for all resources",
|
|
46
|
+
"Maintain entity access matrices for different data types",
|
|
47
|
+
"Use jurisdiction-aware data routing for compliance",
|
|
48
|
+
"Implement progressive disclosure for sensitive data access",
|
|
49
|
+
"Monitor access patterns for unusual behavior",
|
|
50
|
+
"Maintain audit trails for all data access operations",
|
|
51
|
+
"Use encryption for data in transit and at rest",
|
|
52
|
+
"Implement data retention policies based on classification"
|
|
53
|
+
],
|
|
54
|
+
"cache": {
|
|
55
|
+
"default_ttl_seconds": 60,
|
|
56
|
+
"suspend_invalidate_seconds": 30
|
|
57
|
+
},
|
|
58
|
+
"required_context": {
|
|
59
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": [
|
|
62
|
+
"data_classification",
|
|
63
|
+
"accessing_entity_id",
|
|
64
|
+
"accessing_entity_type",
|
|
65
|
+
"resource_id"
|
|
66
|
+
],
|
|
67
|
+
"properties": {
|
|
68
|
+
"data_classification": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "The classification of the data being accessed (e.g., 'PII', 'Financial', 'HR', 'ClientTier1')."
|
|
71
|
+
},
|
|
72
|
+
"accessing_entity_id": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "The unique ID of the entity (user, agent, employee) attempting the access."
|
|
75
|
+
},
|
|
76
|
+
"accessing_entity_type": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"enum": ["employee", "client", "system_agent"],
|
|
79
|
+
"description": "The type of entity attempting the access."
|
|
80
|
+
},
|
|
81
|
+
"resource_id": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"description": "The unique ID of the data resource being accessed (e.g., account number, user profile ID)."
|
|
84
|
+
},
|
|
85
|
+
"action_type": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"enum": ["read", "export", "delete", "update"],
|
|
88
|
+
"default": "read",
|
|
89
|
+
"description": "The type of data access action being performed."
|
|
90
|
+
},
|
|
91
|
+
"jurisdiction": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"pattern": "^[A-Z]{2}$",
|
|
94
|
+
"description": "The ISO 3166-1 alpha-2 country code relevant to the data's jurisdiction."
|
|
95
|
+
},
|
|
96
|
+
"row_count": {
|
|
97
|
+
"type": "integer",
|
|
98
|
+
"description": "The number of rows/records being accessed or exported. Solves for Row Limits."
|
|
99
|
+
},
|
|
100
|
+
"destination_jurisdiction": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"pattern": "^[A-Z]{2}$",
|
|
103
|
+
"description": "The destination country code for data transfers. Solves for Data Locality."
|
|
104
|
+
},
|
|
105
|
+
"resource_attributes": {
|
|
106
|
+
"type": "object",
|
|
107
|
+
"description": "A flexible object for passing specific attributes of the resource being accessed. Solves for Balance Inquiry.",
|
|
108
|
+
"properties": {
|
|
109
|
+
"account_balance_usd": {
|
|
110
|
+
"type": "integer",
|
|
111
|
+
"description": "The balance of the account in USD minor units, used for balance-based access rules."
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"mcp_servers": {
|
|
116
|
+
"type": "array",
|
|
117
|
+
"items": { "type": "string" },
|
|
118
|
+
"description": "MCP servers being used in this request (e.g., [\"https://mcp.snowflake.com\"])"
|
|
119
|
+
},
|
|
120
|
+
"mcp_tools": {
|
|
121
|
+
"type": "array",
|
|
122
|
+
"items": { "type": "string" },
|
|
123
|
+
"description": "MCP tools being used in this request (e.g., [\"snowflake.query.execute\"])"
|
|
124
|
+
},
|
|
125
|
+
"mcp_server": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"description": "Single MCP server being used (backward compatibility - use mcp_servers array for multiple)"
|
|
128
|
+
},
|
|
129
|
+
"mcp_tool": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"description": "Single MCP tool being used (backward compatibility - use mcp_tools array for multiple)"
|
|
132
|
+
},
|
|
133
|
+
"mcp_session": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"description": "MCP session identifier for audit trail (optional)"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"evaluation_rules": [
|
|
140
|
+
{
|
|
141
|
+
"name": "passport_status_active",
|
|
142
|
+
"condition": "passport.status == 'active'",
|
|
143
|
+
"deny_code": "oap.passport_suspended"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "assurance_minimum",
|
|
147
|
+
"condition": "passport.assurance_level >= policy.min_assurance",
|
|
148
|
+
"deny_code": "oap.assurance_insufficient"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"name": "classification_allowed",
|
|
152
|
+
"condition": "context.data_classification in passport.limits.data.access.allowed_classifications",
|
|
153
|
+
"deny_code": "oap.classification_forbidden"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "entity_type_allowed_for_action",
|
|
157
|
+
"condition": "context.accessing_entity_type in passport.limits.data.access.permissions[context.data_classification].allowed_entity_types",
|
|
158
|
+
"deny_code": "oap.entity_type_forbidden"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "jurisdiction_check",
|
|
162
|
+
"condition": "context.jurisdiction in passport.limits.data.access.allowed_jurisdictions",
|
|
163
|
+
"deny_code": "oap.jurisdiction_blocked"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"name": "row_limit_check",
|
|
167
|
+
"condition": "context.row_count <= passport.limits.data.access.max_rows_per_export",
|
|
168
|
+
"deny_code": "oap.row_limit_exceeded",
|
|
169
|
+
"description": "Ensures the number of records in an export does not exceed the defined cap."
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"name": "data_locality_check",
|
|
173
|
+
"condition": "context.destination_jurisdiction in passport.limits.data.access.allowed_destination_jurisdictions",
|
|
174
|
+
"deny_code": "oap.jurisdiction_blocked",
|
|
175
|
+
"description": "Restricts international transfers of sensitive data to approved jurisdictions."
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "balance_inquiry_limit",
|
|
179
|
+
"condition": "not context.resource_attributes.account_balance_usd or context.resource_attributes.account_balance_usd < passport.limits.data.access.balance_inquiry_cap_usd",
|
|
180
|
+
"deny_code": "oap.balance_inquiry_forbidden",
|
|
181
|
+
"description": "Limits agent access to view high-net-worth client balances."
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "assurance_minimum",
|
|
185
|
+
"condition": "passport.assurance_level >= policy.min_assurance",
|
|
186
|
+
"deny_code": "oap.assurance_insufficient",
|
|
187
|
+
"description": "Assurance level must meet minimum requirement for data access."
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"name": "action_type_allowed",
|
|
191
|
+
"condition": "context.action_type in passport.limits.data.access.permissions[context.data_classification].allowed_actions",
|
|
192
|
+
"deny_code": "oap.action_forbidden",
|
|
193
|
+
"description": "Action type must be allowed for the data classification."
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"name": "entity_access_frequency_check",
|
|
197
|
+
"condition": "entity_access_count[context.accessing_entity_id] < passport.limits.data.access.max_access_attempts_per_hour",
|
|
198
|
+
"deny_code": "oap.access_frequency_exceeded",
|
|
199
|
+
"description": "Entity access frequency must not exceed hourly limits."
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "data_retention_check",
|
|
203
|
+
"condition": "context.data_timestamp and (now() - to_timestamp(context.data_timestamp)) <= passport.limits.data.access.max_data_age_seconds",
|
|
204
|
+
"deny_code": "oap.data_expired",
|
|
205
|
+
"description": "Data must not be older than retention policy allows."
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{"name":"allow_pii_read_employee","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"read","jurisdiction":"US"}}
|
|
2
|
+
{"name":"deny_classification_forbidden","context":{"data_classification":"Sensitive","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"read","jurisdiction":"US"}}
|
|
3
|
+
{"name":"deny_entity_type_forbidden","context":{"data_classification":"HR","accessing_entity_id":"client_123","accessing_entity_type":"client","resource_id":"emp_456","action_type":"read","jurisdiction":"US"}}
|
|
4
|
+
{"name":"deny_jurisdiction_blocked","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"read","jurisdiction":"CN"}}
|
|
5
|
+
{"name":"deny_row_limit_exceeded","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"export","jurisdiction":"US","row_count":15000}}
|
|
6
|
+
{"name":"deny_destination_jurisdiction_blocked","context":{"data_classification":"PII","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"user_456","action_type":"export","jurisdiction":"US","destination_jurisdiction":"CN"}}
|
|
7
|
+
{"name":"deny_balance_inquiry_forbidden","context":{"data_classification":"Financial","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"account_456","action_type":"read","jurisdiction":"US","resource_attributes":{"account_balance_usd":150000}}}
|
|
8
|
+
{"name":"allow_financial_export_system_agent","context":{"data_classification":"Financial","accessing_entity_id":"agent_123","accessing_entity_type":"system_agent","resource_id":"account_456","action_type":"export","jurisdiction":"US","row_count":5000}}
|
|
9
|
+
{"name":"allow_client_tier1_read","context":{"data_classification":"ClientTier1","accessing_entity_id":"client_123","accessing_entity_type":"client","resource_id":"client_data_456","action_type":"read","jurisdiction":"US"}}
|
|
10
|
+
{"name":"deny_action_forbidden","context":{"data_classification":"HR","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"emp_456","action_type":"delete","jurisdiction":"US"}}
|
|
11
|
+
{"name":"allow_hr_read_employee","context":{"data_classification":"HR","accessing_entity_id":"emp_123","accessing_entity_type":"employee","resource_id":"emp_456","action_type":"read","jurisdiction":"US"}}
|
|
12
|
+
{"name":"deny_missing_required_fields","context":{"data_classification":"PII","accessing_entity_id":"emp_123","action_type":"read","jurisdiction":"US"}}
|