@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,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test case definitions and types for OAP conformance testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface TestCase {
|
|
6
|
+
id: string;
|
|
7
|
+
packId: string;
|
|
8
|
+
contextName: string;
|
|
9
|
+
passport: any;
|
|
10
|
+
context: any;
|
|
11
|
+
expected: any;
|
|
12
|
+
receipt?: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TestResult {
|
|
16
|
+
testCase: TestCase;
|
|
17
|
+
passed: boolean;
|
|
18
|
+
errors: string[];
|
|
19
|
+
warnings: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ConformanceReport {
|
|
23
|
+
timestamp: string;
|
|
24
|
+
summary: {
|
|
25
|
+
total: number;
|
|
26
|
+
passed: number;
|
|
27
|
+
failed: number;
|
|
28
|
+
successRate: number;
|
|
29
|
+
};
|
|
30
|
+
details: Array<{
|
|
31
|
+
testCase: string;
|
|
32
|
+
passed: boolean;
|
|
33
|
+
errors: string[];
|
|
34
|
+
warnings: string[];
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PolicyPackTestCase {
|
|
39
|
+
packId: string;
|
|
40
|
+
name: string;
|
|
41
|
+
description: string;
|
|
42
|
+
passports: any[];
|
|
43
|
+
contexts: any[];
|
|
44
|
+
expected: any[];
|
|
45
|
+
receipts?: any[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SignatureVerificationResult {
|
|
49
|
+
valid: boolean;
|
|
50
|
+
errors: string[];
|
|
51
|
+
warnings: string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface PolicyEvaluationResult {
|
|
55
|
+
valid: boolean;
|
|
56
|
+
decision: any;
|
|
57
|
+
errors: string[];
|
|
58
|
+
warnings: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Standard test cases for OAP conformance
|
|
63
|
+
*/
|
|
64
|
+
export const STANDARD_TEST_CASES: PolicyPackTestCase[] = [
|
|
65
|
+
{
|
|
66
|
+
packId: "finance.payment.refund.v1",
|
|
67
|
+
name: "Refunds Policy Pack",
|
|
68
|
+
description: "Tests for finance.payment.refund.v1 policy pack",
|
|
69
|
+
passports: [],
|
|
70
|
+
contexts: [],
|
|
71
|
+
expected: [],
|
|
72
|
+
receipts: [],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
packId: "data.export.create.v1",
|
|
76
|
+
name: "Data Export Policy Pack",
|
|
77
|
+
description: "Tests for data.export.create.v1 policy pack",
|
|
78
|
+
passports: [],
|
|
79
|
+
contexts: [],
|
|
80
|
+
expected: [],
|
|
81
|
+
receipts: [],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
packId: "repo.release.publish.v1",
|
|
85
|
+
name: "Repository Release Policy Pack",
|
|
86
|
+
description: "Tests for repo.release.publish.v1 policy pack",
|
|
87
|
+
passports: [],
|
|
88
|
+
contexts: [],
|
|
89
|
+
expected: [],
|
|
90
|
+
receipts: [],
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Error codes for OAP conformance testing
|
|
96
|
+
*/
|
|
97
|
+
export const OAP_ERROR_CODES = {
|
|
98
|
+
INVALID_CONTEXT: "oap.invalid_context",
|
|
99
|
+
UNKNOWN_CAPABILITY: "oap.unknown_capability",
|
|
100
|
+
LIMIT_EXCEEDED: "oap.limit_exceeded",
|
|
101
|
+
CURRENCY_UNSUPPORTED: "oap.currency_unsupported",
|
|
102
|
+
REGION_BLOCKED: "oap.region_blocked",
|
|
103
|
+
ASSURANCE_INSUFFICIENT: "oap.assurance_insufficient",
|
|
104
|
+
PASSPORT_SUSPENDED: "oap.passport_suspended",
|
|
105
|
+
IDEMPOTENCY_CONFLICT: "oap.idempotency_conflict",
|
|
106
|
+
POLICY_ERROR: "oap.policy_error",
|
|
107
|
+
PII_BLOCKED: "oap.pii_blocked",
|
|
108
|
+
COLLECTION_FORBIDDEN: "oap.collection_forbidden",
|
|
109
|
+
BRANCH_FORBIDDEN: "oap.branch_forbidden",
|
|
110
|
+
REPO_FORBIDDEN: "oap.repo_forbidden",
|
|
111
|
+
UNSIGNED_ARTIFACT: "oap.unsigned_artifact",
|
|
112
|
+
} as const;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Assurance levels for OAP conformance testing
|
|
116
|
+
*/
|
|
117
|
+
export const ASSURANCE_LEVELS = {
|
|
118
|
+
L0: "L0",
|
|
119
|
+
L1: "L1",
|
|
120
|
+
L2: "L2",
|
|
121
|
+
L3: "L3",
|
|
122
|
+
L4KYC: "L4KYC",
|
|
123
|
+
L4FIN: "L4FIN",
|
|
124
|
+
} as const;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Policy pack requirements for conformance testing
|
|
128
|
+
*/
|
|
129
|
+
export const POLICY_PACK_REQUIREMENTS = {
|
|
130
|
+
"finance.payment.refund.v1": {
|
|
131
|
+
requiredCapabilities: ["finance.payment.refund"],
|
|
132
|
+
minAssurance: "L2",
|
|
133
|
+
requiredContextFields: ["amount", "currency", "order_id"],
|
|
134
|
+
optionalContextFields: [
|
|
135
|
+
"customer_id",
|
|
136
|
+
"reason_code",
|
|
137
|
+
"region",
|
|
138
|
+
"idempotency_key",
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
"data.export.create.v1": {
|
|
142
|
+
requiredCapabilities: ["data.export"],
|
|
143
|
+
minAssurance: "L1",
|
|
144
|
+
requiredContextFields: ["collection"],
|
|
145
|
+
optionalContextFields: ["estimated_rows", "include_pii", "region"],
|
|
146
|
+
},
|
|
147
|
+
"repo.release.publish.v1": {
|
|
148
|
+
requiredCapabilities: ["repo.release.publish"],
|
|
149
|
+
minAssurance: "L2",
|
|
150
|
+
requiredContextFields: ["repo", "branch", "tag"],
|
|
151
|
+
optionalContextFields: ["artifact_sha", "signer"],
|
|
152
|
+
},
|
|
153
|
+
} as const;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Test case validation utilities
|
|
157
|
+
*/
|
|
158
|
+
export class TestCaseValidator {
|
|
159
|
+
/**
|
|
160
|
+
* Validate a test case structure
|
|
161
|
+
*/
|
|
162
|
+
static validateTestCase(testCase: TestCase): {
|
|
163
|
+
valid: boolean;
|
|
164
|
+
errors: string[];
|
|
165
|
+
} {
|
|
166
|
+
const errors: string[] = [];
|
|
167
|
+
|
|
168
|
+
if (!testCase.id) {
|
|
169
|
+
errors.push("Test case ID is required");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!testCase.packId) {
|
|
173
|
+
errors.push("Policy pack ID is required");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!testCase.passport) {
|
|
177
|
+
errors.push("Passport is required");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!testCase.context) {
|
|
181
|
+
errors.push("Context is required");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!testCase.expected) {
|
|
185
|
+
errors.push("Expected result is required");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
valid: errors.length === 0,
|
|
190
|
+
errors,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Validate a policy pack test case
|
|
196
|
+
*/
|
|
197
|
+
static validatePolicyPackTestCase(testCase: PolicyPackTestCase): {
|
|
198
|
+
valid: boolean;
|
|
199
|
+
errors: string[];
|
|
200
|
+
} {
|
|
201
|
+
const errors: string[] = [];
|
|
202
|
+
|
|
203
|
+
if (!testCase.packId) {
|
|
204
|
+
errors.push("Policy pack ID is required");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!testCase.name) {
|
|
208
|
+
errors.push("Test case name is required");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!Array.isArray(testCase.passports)) {
|
|
212
|
+
errors.push("Passports must be an array");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!Array.isArray(testCase.contexts)) {
|
|
216
|
+
errors.push("Contexts must be an array");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!Array.isArray(testCase.expected)) {
|
|
220
|
+
errors.push("Expected results must be an array");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (testCase.passports.length !== testCase.contexts.length) {
|
|
224
|
+
errors.push("Number of passports must match number of contexts");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (testCase.contexts.length !== testCase.expected.length) {
|
|
228
|
+
errors.push("Number of contexts must match number of expected results");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
valid: errors.length === 0,
|
|
233
|
+
errors,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Test case generators for common scenarios
|
|
240
|
+
*/
|
|
241
|
+
export class TestCaseGenerator {
|
|
242
|
+
/**
|
|
243
|
+
* Generate a basic refund test case
|
|
244
|
+
*/
|
|
245
|
+
static generateRefundTestCase(
|
|
246
|
+
amount: number,
|
|
247
|
+
currency: string,
|
|
248
|
+
shouldPass: boolean
|
|
249
|
+
): TestCase {
|
|
250
|
+
return {
|
|
251
|
+
id: `finance.payment.refund.v1:${amount}${currency}_${
|
|
252
|
+
shouldPass ? "allow" : "deny"
|
|
253
|
+
}`,
|
|
254
|
+
packId: "finance.payment.refund.v1",
|
|
255
|
+
contextName: `${amount}${currency}_${shouldPass ? "allow" : "deny"}`,
|
|
256
|
+
passport: {
|
|
257
|
+
passport_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
258
|
+
kind: "template",
|
|
259
|
+
spec_version: "oap/1.0",
|
|
260
|
+
owner_id: "org_12345678",
|
|
261
|
+
owner_type: "org",
|
|
262
|
+
assurance_level: "L2",
|
|
263
|
+
status: "active",
|
|
264
|
+
capabilities: [{ id: "finance.payment.refund" }],
|
|
265
|
+
limits: {
|
|
266
|
+
"finance.payment.refund": {
|
|
267
|
+
currency_limits: {
|
|
268
|
+
[currency]: {
|
|
269
|
+
max_per_tx: shouldPass ? amount * 2 : amount / 2,
|
|
270
|
+
daily_cap: amount * 10,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
regions: ["US"],
|
|
276
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
277
|
+
updated_at: "2024-01-15T10:30:00Z",
|
|
278
|
+
version: "1.0.0",
|
|
279
|
+
},
|
|
280
|
+
context: {
|
|
281
|
+
amount,
|
|
282
|
+
currency,
|
|
283
|
+
order_id: `order_${Date.now()}`,
|
|
284
|
+
customer_id: "cust_123",
|
|
285
|
+
reason_code: "customer_request",
|
|
286
|
+
region: "US",
|
|
287
|
+
},
|
|
288
|
+
expected: {
|
|
289
|
+
decision_id: "test_decision_id",
|
|
290
|
+
policy_id: "finance.payment.refund.v1",
|
|
291
|
+
agent_id: "550e8400-e29b-41d4-a716-446655440000",
|
|
292
|
+
owner_id: "org_12345678",
|
|
293
|
+
assurance_level: "L2",
|
|
294
|
+
allow: shouldPass,
|
|
295
|
+
reasons: shouldPass
|
|
296
|
+
? [{ code: "oap.allowed", message: "Transaction within limits" }]
|
|
297
|
+
: [{ code: "oap.limit_exceeded", message: "Amount exceeds limit" }],
|
|
298
|
+
created_at: "2024-01-15T10:30:00Z",
|
|
299
|
+
expires_in: 3600,
|
|
300
|
+
passport_digest: "sha256:test_digest",
|
|
301
|
+
signature: "ed25519:test_signature",
|
|
302
|
+
kid: "oap:registry:test-key",
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Generate a data export test case
|
|
309
|
+
*/
|
|
310
|
+
static generateDataExportTestCase(
|
|
311
|
+
collection: string,
|
|
312
|
+
estimatedRows: number,
|
|
313
|
+
includePii: boolean,
|
|
314
|
+
shouldPass: boolean
|
|
315
|
+
): TestCase {
|
|
316
|
+
return {
|
|
317
|
+
id: `data.export.create.v1:${collection}_${estimatedRows}_${
|
|
318
|
+
includePii ? "pii" : "no_pii"
|
|
319
|
+
}_${shouldPass ? "allow" : "deny"}`,
|
|
320
|
+
packId: "data.export.create.v1",
|
|
321
|
+
contextName: `${collection}_${estimatedRows}_${
|
|
322
|
+
includePii ? "pii" : "no_pii"
|
|
323
|
+
}_${shouldPass ? "allow" : "deny"}`,
|
|
324
|
+
passport: {
|
|
325
|
+
passport_id: "550e8400-e29b-41d4-a716-446655440001",
|
|
326
|
+
kind: "template",
|
|
327
|
+
spec_version: "oap/1.0",
|
|
328
|
+
owner_id: "org_12345678",
|
|
329
|
+
owner_type: "org",
|
|
330
|
+
assurance_level: "L1",
|
|
331
|
+
status: "active",
|
|
332
|
+
capabilities: [{ id: "data.export" }],
|
|
333
|
+
limits: {
|
|
334
|
+
"data.export": {
|
|
335
|
+
max_rows: shouldPass ? estimatedRows * 2 : estimatedRows / 2,
|
|
336
|
+
allow_pii: includePii,
|
|
337
|
+
allowed_collections: shouldPass
|
|
338
|
+
? [collection]
|
|
339
|
+
: ["other_collection"],
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
regions: ["US"],
|
|
343
|
+
created_at: "2024-01-01T00:00:00Z",
|
|
344
|
+
updated_at: "2024-01-15T10:30:00Z",
|
|
345
|
+
version: "1.0.0",
|
|
346
|
+
},
|
|
347
|
+
context: {
|
|
348
|
+
collection,
|
|
349
|
+
estimated_rows: estimatedRows,
|
|
350
|
+
include_pii: includePii,
|
|
351
|
+
region: "US",
|
|
352
|
+
},
|
|
353
|
+
expected: {
|
|
354
|
+
decision_id: "test_decision_id",
|
|
355
|
+
policy_id: "data.export.create.v1",
|
|
356
|
+
agent_id: "550e8400-e29b-41d4-a716-446655440001",
|
|
357
|
+
owner_id: "org_12345678",
|
|
358
|
+
assurance_level: "L1",
|
|
359
|
+
allow: shouldPass,
|
|
360
|
+
reasons: shouldPass
|
|
361
|
+
? [{ code: "oap.allowed", message: "Export within limits" }]
|
|
362
|
+
: [{ code: "oap.limit_exceeded", message: "Row limit exceeded" }],
|
|
363
|
+
created_at: "2024-01-15T10:30:00Z",
|
|
364
|
+
expires_in: 3600,
|
|
365
|
+
passport_digest: "sha256:test_digest",
|
|
366
|
+
signature: "ed25519:test_signature",
|
|
367
|
+
kid: "oap:registry:test-key",
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ed25519 signature verification for OAP conformance testing
|
|
3
|
+
*
|
|
4
|
+
* Note: This is a simplified implementation for conformance testing.
|
|
5
|
+
* In production, use a proper Ed25519 library like @noble/ed25519
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class Ed25519 {
|
|
9
|
+
/**
|
|
10
|
+
* Verify an Ed25519 signature
|
|
11
|
+
*
|
|
12
|
+
* @param message - The message that was signed
|
|
13
|
+
* @param signature - The signature in base64 format
|
|
14
|
+
* @param publicKey - The public key in base64 format
|
|
15
|
+
* @returns Promise<boolean> - True if signature is valid
|
|
16
|
+
*/
|
|
17
|
+
async verify(
|
|
18
|
+
message: Uint8Array,
|
|
19
|
+
signature: string,
|
|
20
|
+
publicKey: string
|
|
21
|
+
): Promise<boolean> {
|
|
22
|
+
try {
|
|
23
|
+
// For conformance testing, we'll implement a simplified verification
|
|
24
|
+
// In production, this would use a proper Ed25519 library
|
|
25
|
+
|
|
26
|
+
// Remove the 'ed25519:' prefix if present
|
|
27
|
+
const cleanSignature = signature.replace(/^ed25519:/, "");
|
|
28
|
+
const cleanPublicKey = publicKey.replace(/^ed25519:/, "");
|
|
29
|
+
|
|
30
|
+
// Basic format validation
|
|
31
|
+
if (
|
|
32
|
+
!this.isValidBase64(cleanSignature) ||
|
|
33
|
+
!this.isValidBase64(cleanPublicKey)
|
|
34
|
+
) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// For conformance testing, we'll accept any properly formatted signature
|
|
39
|
+
// In production, this would perform actual cryptographic verification
|
|
40
|
+
return cleanSignature.length === 88 && cleanPublicKey.length === 44; // Expected lengths for base64 encoded Ed25519
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.warn("Ed25519 verification failed:", error);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Verify a decision signature
|
|
49
|
+
*
|
|
50
|
+
* @param decision - The decision object
|
|
51
|
+
* @param signature - The signature string
|
|
52
|
+
* @param publicKey - The public key
|
|
53
|
+
* @returns Promise<boolean> - True if signature is valid
|
|
54
|
+
*/
|
|
55
|
+
async verifyDecisionSignature(
|
|
56
|
+
decision: any,
|
|
57
|
+
signature: string,
|
|
58
|
+
publicKey: string
|
|
59
|
+
): Promise<boolean> {
|
|
60
|
+
try {
|
|
61
|
+
// Create the message that was signed (JCS canonicalized decision)
|
|
62
|
+
const jcs = new (await import("./jcs.js")).JCS();
|
|
63
|
+
const canonical = jcs.canonicalize(decision);
|
|
64
|
+
const message = new TextEncoder().encode(canonical);
|
|
65
|
+
|
|
66
|
+
return await this.verify(message, signature, publicKey);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.warn("Decision signature verification failed:", error);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Verify a passport digest signature
|
|
75
|
+
*
|
|
76
|
+
* @param passport - The passport object
|
|
77
|
+
* @param digest - The passport digest
|
|
78
|
+
* @returns Promise<boolean> - True if digest is valid
|
|
79
|
+
*/
|
|
80
|
+
async verifyPassportDigest(passport: any, digest: string): Promise<boolean> {
|
|
81
|
+
try {
|
|
82
|
+
const jcs = new (await import("./jcs.js")).JCS();
|
|
83
|
+
const computedDigest = await jcs.computePassportDigest(passport);
|
|
84
|
+
return computedDigest === digest;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.warn("Passport digest verification failed:", error);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Generate a test key pair for conformance testing
|
|
93
|
+
*
|
|
94
|
+
* @returns Promise<{publicKey: string, privateKey: string}>
|
|
95
|
+
*/
|
|
96
|
+
async generateTestKeyPair(): Promise<{
|
|
97
|
+
publicKey: string;
|
|
98
|
+
privateKey: string;
|
|
99
|
+
}> {
|
|
100
|
+
// For conformance testing, generate deterministic test keys
|
|
101
|
+
// In production, use proper key generation
|
|
102
|
+
const publicKey = "ed25519:" + "A".repeat(44); // 44 chars for base64 encoded 32-byte key
|
|
103
|
+
const privateKey = "ed25519:" + "B".repeat(44);
|
|
104
|
+
|
|
105
|
+
return { publicKey, privateKey };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Sign a message with a private key (for testing)
|
|
110
|
+
*
|
|
111
|
+
* @param message - The message to sign
|
|
112
|
+
* @param privateKey - The private key
|
|
113
|
+
* @returns Promise<string> - The signature
|
|
114
|
+
*/
|
|
115
|
+
async sign(message: Uint8Array, privateKey: string): Promise<string> {
|
|
116
|
+
// For conformance testing, generate a deterministic signature
|
|
117
|
+
// In production, use proper Ed25519 signing
|
|
118
|
+
const messageStr = new TextDecoder().decode(message);
|
|
119
|
+
const hash = await crypto.subtle.digest("SHA-256", new Uint8Array(message));
|
|
120
|
+
const hashArray = Array.from(new Uint8Array(hash));
|
|
121
|
+
const hashHex = hashArray
|
|
122
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
123
|
+
.join("");
|
|
124
|
+
return "ed25519:" + hashHex.substring(0, 88); // 88 chars for base64 encoded 64-byte signature
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if a string is valid base64
|
|
129
|
+
*/
|
|
130
|
+
private isValidBase64(str: string): boolean {
|
|
131
|
+
try {
|
|
132
|
+
return btoa(atob(str)) === str;
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Resolve a key ID to a public key
|
|
140
|
+
*
|
|
141
|
+
* @param kid - The key identifier
|
|
142
|
+
* @returns Promise<string | null> - The public key or null if not found
|
|
143
|
+
*/
|
|
144
|
+
async resolveKey(kid: string): Promise<string | null> {
|
|
145
|
+
try {
|
|
146
|
+
// For conformance testing, we'll use test keys
|
|
147
|
+
// In production, this would resolve keys from /.well-known/oap/keys.json
|
|
148
|
+
|
|
149
|
+
if (kid.startsWith("oap:registry:test-key")) {
|
|
150
|
+
const { publicKey } = await this.generateTestKeyPair();
|
|
151
|
+
return publicKey;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (kid.startsWith("oap:owner:")) {
|
|
155
|
+
// For owner keys, we'd fetch from the owner's domain
|
|
156
|
+
// For conformance testing, return a test key
|
|
157
|
+
const { publicKey } = await this.generateTestKeyPair();
|
|
158
|
+
return publicKey;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return null;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.warn("Key resolution failed:", error);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Canonicalization Scheme (JCS) implementation
|
|
3
|
+
*
|
|
4
|
+
* Implements RFC 8785 for deterministic JSON serialization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export class JCS {
|
|
8
|
+
/**
|
|
9
|
+
* Canonicalize a JSON object according to RFC 8785
|
|
10
|
+
*/
|
|
11
|
+
canonicalize(obj: any): string {
|
|
12
|
+
return this.canonicalizeValue(obj);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private canonicalizeValue(value: any): string {
|
|
16
|
+
if (value === null) {
|
|
17
|
+
return "null";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (typeof value === "boolean") {
|
|
21
|
+
return value ? "true" : "false";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof value === "number") {
|
|
25
|
+
if (Number.isInteger(value)) {
|
|
26
|
+
return value.toString();
|
|
27
|
+
}
|
|
28
|
+
return value.toString();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof value === "string") {
|
|
32
|
+
return JSON.stringify(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
return this.canonicalizeArray(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof value === "object") {
|
|
40
|
+
return this.canonicalizeObject(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
throw new Error(`Unsupported value type: ${typeof value}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private canonicalizeArray(arr: any[]): string {
|
|
47
|
+
const elements = arr.map((item) => this.canonicalizeValue(item));
|
|
48
|
+
return "[" + elements.join(",") + "]";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private canonicalizeObject(obj: any): string {
|
|
52
|
+
// Get all keys and sort them
|
|
53
|
+
const keys = Object.keys(obj).sort();
|
|
54
|
+
|
|
55
|
+
const pairs = keys.map((key) => {
|
|
56
|
+
const value = this.canonicalizeValue(obj[key]);
|
|
57
|
+
return JSON.stringify(key) + ":" + value;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return "{" + pairs.join(",") + "}";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Compute SHA-256 hash of canonicalized JSON
|
|
65
|
+
*/
|
|
66
|
+
async computeHash(obj: any): Promise<string> {
|
|
67
|
+
const canonical = this.canonicalize(obj);
|
|
68
|
+
const encoder = new TextEncoder();
|
|
69
|
+
const data = encoder.encode(canonical);
|
|
70
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
71
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
72
|
+
const hashHex = hashArray
|
|
73
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
74
|
+
.join("");
|
|
75
|
+
return hashHex;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Compute passport digest as specified in OAP
|
|
80
|
+
*/
|
|
81
|
+
async computePassportDigest(passport: any): Promise<string> {
|
|
82
|
+
const hash = await this.computeHash(passport);
|
|
83
|
+
return `sha256:${hash}`;
|
|
84
|
+
}
|
|
85
|
+
}
|