@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,533 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OAP Conformance Test Runner
|
|
5
|
+
*
|
|
6
|
+
* Validates OAP implementations against the specification by:
|
|
7
|
+
* - Validating JSON against schemas
|
|
8
|
+
* - Evaluating policy pack logic
|
|
9
|
+
* - Verifying Ed25519 signatures over JCS payloads
|
|
10
|
+
* - Producing PASS/FAIL reports
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Command } from "commander";
|
|
14
|
+
import chalk from "chalk";
|
|
15
|
+
import ora from "ora";
|
|
16
|
+
import {
|
|
17
|
+
readFileSync,
|
|
18
|
+
writeFileSync,
|
|
19
|
+
mkdirSync,
|
|
20
|
+
existsSync,
|
|
21
|
+
readdirSync,
|
|
22
|
+
} from "fs";
|
|
23
|
+
import { join, dirname } from "path";
|
|
24
|
+
import { fileURLToPath } from "url";
|
|
25
|
+
|
|
26
|
+
import { SchemaValidator } from "./validators.js";
|
|
27
|
+
import { JCS } from "./jcs.js";
|
|
28
|
+
import { Ed25519 } from "./ed25519.js";
|
|
29
|
+
import { TestCase, TestResult, ConformanceReport } from "./cases.js";
|
|
30
|
+
|
|
31
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
32
|
+
const __dirname = dirname(__filename);
|
|
33
|
+
|
|
34
|
+
interface RunnerOptions {
|
|
35
|
+
pack?: string;
|
|
36
|
+
verbose?: boolean;
|
|
37
|
+
report?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class ConformanceRunner {
|
|
41
|
+
private validator: SchemaValidator;
|
|
42
|
+
private jcs: JCS;
|
|
43
|
+
private ed25519: Ed25519;
|
|
44
|
+
private options: RunnerOptions;
|
|
45
|
+
|
|
46
|
+
constructor(options: RunnerOptions = {}) {
|
|
47
|
+
this.options = options;
|
|
48
|
+
this.validator = new SchemaValidator();
|
|
49
|
+
this.jcs = new JCS();
|
|
50
|
+
this.ed25519 = new Ed25519();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async run(): Promise<void> {
|
|
54
|
+
console.log(chalk.blue.bold("\n🔍 OAP Conformance Test Runner v1.0.0\n"));
|
|
55
|
+
|
|
56
|
+
const spinner = ora("Loading test cases...").start();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Load test cases
|
|
60
|
+
const testCases = await this.loadTestCases();
|
|
61
|
+
spinner.succeed(`Loaded ${testCases.length} test cases`);
|
|
62
|
+
|
|
63
|
+
// Run tests
|
|
64
|
+
const results = await this.runTests(testCases);
|
|
65
|
+
|
|
66
|
+
// Generate report
|
|
67
|
+
const report = this.generateReport(results);
|
|
68
|
+
|
|
69
|
+
// Display results
|
|
70
|
+
this.displayResults(report);
|
|
71
|
+
|
|
72
|
+
// Save report if requested
|
|
73
|
+
if (this.options.report) {
|
|
74
|
+
await this.saveReport(report);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Exit with appropriate code
|
|
78
|
+
process.exit(report.summary.failed > 0 ? 1 : 0);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
spinner.fail("Test execution failed");
|
|
81
|
+
console.error(chalk.red("Error:"), error);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async loadTestCases(): Promise<TestCase[]> {
|
|
87
|
+
const casesDir = join(__dirname, "..", "cases");
|
|
88
|
+
const testCases: TestCase[] = [];
|
|
89
|
+
|
|
90
|
+
// Load policy pack test cases
|
|
91
|
+
const packDirs = this.getPackDirectories(casesDir);
|
|
92
|
+
|
|
93
|
+
for (const packDir of packDirs) {
|
|
94
|
+
const packId = packDir.split("/").pop()!;
|
|
95
|
+
|
|
96
|
+
// Skip if specific pack requested and doesn't match
|
|
97
|
+
if (this.options.pack && !packId.includes(this.options.pack)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const packCases = await this.loadPackTestCases(packDir, packId);
|
|
102
|
+
testCases.push(...packCases);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return testCases;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private getPackDirectories(casesDir: string): string[] {
|
|
109
|
+
if (!existsSync(casesDir)) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return readdirSync(casesDir, { withFileTypes: true })
|
|
114
|
+
.filter((dirent: any) => dirent.isDirectory())
|
|
115
|
+
.map((dirent: any) => join(casesDir, dirent.name));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private async loadPackTestCases(
|
|
119
|
+
packDir: string,
|
|
120
|
+
packId: string
|
|
121
|
+
): Promise<TestCase[]> {
|
|
122
|
+
const testCases: TestCase[] = [];
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// Load passports
|
|
126
|
+
const passportsDir = join(packDir, "passports");
|
|
127
|
+
const contextsDir = join(packDir, "contexts");
|
|
128
|
+
const expectedDir = join(packDir, "expected");
|
|
129
|
+
const receiptsDir = join(packDir, "receipts");
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
!existsSync(passportsDir) ||
|
|
133
|
+
!existsSync(contextsDir) ||
|
|
134
|
+
!existsSync(expectedDir)
|
|
135
|
+
) {
|
|
136
|
+
console.warn(
|
|
137
|
+
chalk.yellow(`⚠️ Skipping ${packId}: missing required directories`)
|
|
138
|
+
);
|
|
139
|
+
return testCases;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Load passport files
|
|
143
|
+
const passportFiles = this.getJsonFiles(passportsDir);
|
|
144
|
+
const contextFiles = this.getJsonFiles(contextsDir);
|
|
145
|
+
const expectedFiles = this.getJsonFiles(expectedDir);
|
|
146
|
+
const receiptFiles = existsSync(receiptsDir)
|
|
147
|
+
? this.getJsonFiles(receiptsDir)
|
|
148
|
+
: [];
|
|
149
|
+
|
|
150
|
+
// Create test cases
|
|
151
|
+
for (const contextFile of contextFiles) {
|
|
152
|
+
const contextName = contextFile.replace(".json", "");
|
|
153
|
+
const expectedFile = expectedFiles.find((f) => f.includes(contextName));
|
|
154
|
+
|
|
155
|
+
if (!expectedFile) {
|
|
156
|
+
console.warn(
|
|
157
|
+
chalk.yellow(`⚠️ No expected result for context: ${contextName}`)
|
|
158
|
+
);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const testCase: TestCase = {
|
|
163
|
+
id: `${packId}:${contextName}`,
|
|
164
|
+
packId,
|
|
165
|
+
contextName,
|
|
166
|
+
passport: this.loadJsonFile(join(passportsDir, passportFiles[0])), // Use first passport
|
|
167
|
+
context: this.loadJsonFile(join(contextsDir, contextFile)),
|
|
168
|
+
expected: this.loadJsonFile(join(expectedDir, expectedFile)),
|
|
169
|
+
receipt: receiptFiles.find((f) => f.includes(contextName))
|
|
170
|
+
? this.loadJsonFile(
|
|
171
|
+
join(
|
|
172
|
+
receiptsDir,
|
|
173
|
+
receiptFiles.find((f) => f.includes(contextName))!
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
: undefined,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
testCases.push(testCase);
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.warn(chalk.yellow(`⚠️ Error loading ${packId}: ${error}`));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return testCases;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private getJsonFiles(dir: string): string[] {
|
|
189
|
+
return readdirSync(dir).filter((file: string) => file.endsWith(".json"));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private loadJsonFile(filePath: string): any {
|
|
193
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private async runTests(testCases: TestCase[]): Promise<TestResult[]> {
|
|
197
|
+
const results: TestResult[] = [];
|
|
198
|
+
|
|
199
|
+
for (const testCase of testCases) {
|
|
200
|
+
const spinner = ora(`Running ${testCase.id}...`).start();
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const result = await this.runTestCase(testCase);
|
|
204
|
+
results.push(result);
|
|
205
|
+
|
|
206
|
+
if (result.passed) {
|
|
207
|
+
spinner.succeed(`${testCase.id}: PASS`);
|
|
208
|
+
} else {
|
|
209
|
+
spinner.fail(`${testCase.id}: FAIL`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (this.options.verbose && !result.passed) {
|
|
213
|
+
console.log(chalk.red(" Errors:"), result.errors);
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
spinner.fail(`${testCase.id}: ERROR`);
|
|
217
|
+
results.push({
|
|
218
|
+
testCase,
|
|
219
|
+
passed: false,
|
|
220
|
+
errors: [`Test execution failed: ${error}`],
|
|
221
|
+
warnings: [],
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return results;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async runTestCase(testCase: TestCase): Promise<TestResult> {
|
|
230
|
+
const errors: string[] = [];
|
|
231
|
+
const warnings: string[] = [];
|
|
232
|
+
|
|
233
|
+
// 1. Validate passport against schema
|
|
234
|
+
const passportValidation = await this.validator.validatePassport(
|
|
235
|
+
testCase.passport
|
|
236
|
+
);
|
|
237
|
+
if (!passportValidation.valid) {
|
|
238
|
+
errors.push(
|
|
239
|
+
`Passport validation failed: ${passportValidation.errors.join(", ")}`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 2. Validate context against policy requirements
|
|
244
|
+
const contextValidation = await this.validator.validateContext(
|
|
245
|
+
testCase.packId,
|
|
246
|
+
testCase.context
|
|
247
|
+
);
|
|
248
|
+
if (!contextValidation.valid) {
|
|
249
|
+
errors.push(
|
|
250
|
+
`Context validation failed: ${contextValidation.errors.join(", ")}`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 3. Evaluate policy logic (simplified - in real implementation this would call the policy evaluator)
|
|
255
|
+
const policyResult = await this.evaluatePolicy(testCase);
|
|
256
|
+
if (!policyResult.valid) {
|
|
257
|
+
errors.push(
|
|
258
|
+
`Policy evaluation failed: ${policyResult.errors.join(", ")}`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// 4. Validate expected decision against schema
|
|
263
|
+
const decisionValidation = await this.validator.validateDecision(
|
|
264
|
+
testCase.expected
|
|
265
|
+
);
|
|
266
|
+
if (!decisionValidation.valid) {
|
|
267
|
+
errors.push(
|
|
268
|
+
`Expected decision validation failed: ${decisionValidation.errors.join(
|
|
269
|
+
", "
|
|
270
|
+
)}`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 5. Verify signature if receipt provided
|
|
275
|
+
if (testCase.receipt) {
|
|
276
|
+
const signatureValidation = await this.verifySignature(testCase.receipt);
|
|
277
|
+
if (!signatureValidation.valid) {
|
|
278
|
+
errors.push(
|
|
279
|
+
`Signature verification failed: ${signatureValidation.errors.join(
|
|
280
|
+
", "
|
|
281
|
+
)}`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 6. Compare actual vs expected (simplified)
|
|
287
|
+
const comparison = this.compareResults(
|
|
288
|
+
policyResult.decision,
|
|
289
|
+
testCase.expected
|
|
290
|
+
);
|
|
291
|
+
if (!comparison.matches) {
|
|
292
|
+
errors.push(`Result mismatch: ${comparison.differences.join(", ")}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
testCase,
|
|
297
|
+
passed: errors.length === 0,
|
|
298
|
+
errors,
|
|
299
|
+
warnings,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private async evaluatePolicy(
|
|
304
|
+
testCase: TestCase
|
|
305
|
+
): Promise<{ valid: boolean; errors: string[]; decision: any }> {
|
|
306
|
+
// This is a simplified policy evaluation
|
|
307
|
+
// In a real implementation, this would call the actual policy evaluator
|
|
308
|
+
|
|
309
|
+
const { packId, passport, context } = testCase;
|
|
310
|
+
|
|
311
|
+
// Basic policy logic based on pack ID
|
|
312
|
+
let allow = true;
|
|
313
|
+
const reasons: any[] = [];
|
|
314
|
+
|
|
315
|
+
if (packId === "finance.payment.refund.v1") {
|
|
316
|
+
const amount = context.amount || 0;
|
|
317
|
+
const currency = context.currency || "USD";
|
|
318
|
+
|
|
319
|
+
// Check currency limits
|
|
320
|
+
const limits =
|
|
321
|
+
passport.limits?.["finance.payment.refund"]?.currency_limits?.[
|
|
322
|
+
currency
|
|
323
|
+
];
|
|
324
|
+
if (limits) {
|
|
325
|
+
if (amount > limits.max_per_tx) {
|
|
326
|
+
allow = false;
|
|
327
|
+
reasons.push({
|
|
328
|
+
code: "oap.limit_exceeded",
|
|
329
|
+
message: `Amount ${amount} exceeds max per transaction ${limits.max_per_tx}`,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
// Currency not supported
|
|
334
|
+
allow = false;
|
|
335
|
+
reasons.push({
|
|
336
|
+
code: "oap.currency_unsupported",
|
|
337
|
+
message: `Currency ${currency} not supported for this passport`,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (packId === "data.export.create.v1") {
|
|
343
|
+
const includePii = context.include_pii || false;
|
|
344
|
+
const allowPii = passport.limits?.["data.export"]?.allow_pii || false;
|
|
345
|
+
|
|
346
|
+
if (includePii && !allowPii) {
|
|
347
|
+
allow = false;
|
|
348
|
+
reasons.push({
|
|
349
|
+
code: "oap.pii_blocked",
|
|
350
|
+
message: "PII export not allowed for this passport",
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// If no reasons and allow is true, add success reason
|
|
356
|
+
if (allow && reasons.length === 0) {
|
|
357
|
+
reasons.push({
|
|
358
|
+
code: "oap.allowed",
|
|
359
|
+
message: "Transaction within limits and policy requirements",
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const decision = {
|
|
364
|
+
decision_id: `test_${Date.now()}`,
|
|
365
|
+
policy_id: packId,
|
|
366
|
+
agent_id: passport.agent_id || passport.passport_id,
|
|
367
|
+
owner_id: passport.owner_id,
|
|
368
|
+
assurance_level: passport.assurance_level,
|
|
369
|
+
allow,
|
|
370
|
+
reasons,
|
|
371
|
+
created_at: new Date().toISOString(),
|
|
372
|
+
expires_in: 3600,
|
|
373
|
+
passport_digest: await this.computePassportDigest(passport),
|
|
374
|
+
signature:
|
|
375
|
+
"ed25519:test_signature_placeholder_64_chars_long_for_conformance_testing",
|
|
376
|
+
kid: "oap:registry:test-key",
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
valid: true,
|
|
381
|
+
errors: [],
|
|
382
|
+
decision,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private async computePassportDigest(passport: any): Promise<string> {
|
|
387
|
+
const canonical = this.jcs.canonicalize(passport);
|
|
388
|
+
const hash = await crypto.subtle.digest(
|
|
389
|
+
"SHA-256",
|
|
390
|
+
new TextEncoder().encode(canonical)
|
|
391
|
+
);
|
|
392
|
+
const hashArray = Array.from(new Uint8Array(hash));
|
|
393
|
+
const hashHex = hashArray
|
|
394
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
395
|
+
.join("");
|
|
396
|
+
return `sha256:${hashHex}`;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private async verifySignature(
|
|
400
|
+
receipt: any
|
|
401
|
+
): Promise<{ valid: boolean; errors: string[] }> {
|
|
402
|
+
// Simplified signature verification for conformance testing
|
|
403
|
+
// In a real implementation, this would verify Ed25519 signatures
|
|
404
|
+
|
|
405
|
+
if (!receipt.signature) {
|
|
406
|
+
return { valid: false, errors: ["No signature provided"] };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// For conformance testing, we'll just check that signature exists and has correct format
|
|
410
|
+
if (!receipt.signature.startsWith("ed25519:")) {
|
|
411
|
+
return { valid: false, errors: ["Invalid signature format"] };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return { valid: true, errors: [] };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private compareResults(
|
|
418
|
+
actual: any,
|
|
419
|
+
expected: any
|
|
420
|
+
): { matches: boolean; differences: string[] } {
|
|
421
|
+
const differences: string[] = [];
|
|
422
|
+
|
|
423
|
+
// Compare key fields
|
|
424
|
+
if (actual.allow !== expected.allow) {
|
|
425
|
+
differences.push(
|
|
426
|
+
`allow: expected ${expected.allow}, got ${actual.allow}`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (actual.assurance_level !== expected.assurance_level) {
|
|
431
|
+
differences.push(
|
|
432
|
+
`assurance_level: expected ${expected.assurance_level}, got ${actual.assurance_level}`
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Compare reasons (simplified)
|
|
437
|
+
if (actual.reasons?.length !== expected.reasons?.length) {
|
|
438
|
+
differences.push(
|
|
439
|
+
`reasons: expected ${expected.reasons?.length} reasons, got ${actual.reasons?.length}`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
matches: differences.length === 0,
|
|
445
|
+
differences,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private generateReport(results: TestResult[]): ConformanceReport {
|
|
450
|
+
const passed = results.filter((r) => r.passed).length;
|
|
451
|
+
const failed = results.filter((r) => !r.passed).length;
|
|
452
|
+
const total = results.length;
|
|
453
|
+
|
|
454
|
+
const summary = {
|
|
455
|
+
total,
|
|
456
|
+
passed,
|
|
457
|
+
failed,
|
|
458
|
+
successRate: total > 0 ? (passed / total) * 100 : 0,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const details = results.map((result) => ({
|
|
462
|
+
testCase: result.testCase.id,
|
|
463
|
+
passed: result.passed,
|
|
464
|
+
errors: result.errors,
|
|
465
|
+
warnings: result.warnings,
|
|
466
|
+
}));
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
timestamp: new Date().toISOString(),
|
|
470
|
+
summary,
|
|
471
|
+
details,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private displayResults(report: ConformanceReport): void {
|
|
476
|
+
console.log("\n" + chalk.blue.bold("📊 Conformance Test Results\n"));
|
|
477
|
+
|
|
478
|
+
const { summary } = report;
|
|
479
|
+
|
|
480
|
+
console.log(chalk.green(`✅ Passed: ${summary.passed}`));
|
|
481
|
+
console.log(chalk.red(`❌ Failed: ${summary.failed}`));
|
|
482
|
+
console.log(
|
|
483
|
+
chalk.blue(`📈 Success Rate: ${summary.successRate.toFixed(1)}%`)
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
if (summary.failed > 0) {
|
|
487
|
+
console.log("\n" + chalk.red.bold("Failed Tests:"));
|
|
488
|
+
report.details
|
|
489
|
+
.filter((d: any) => !d.passed)
|
|
490
|
+
.forEach((detail: any) => {
|
|
491
|
+
console.log(chalk.red(` • ${detail.testCase}`));
|
|
492
|
+
detail.errors.forEach((error: any) => {
|
|
493
|
+
console.log(chalk.gray(` - ${error}`));
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
console.log("\n" + chalk.blue("🎯 Conformance testing complete!\n"));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private async saveReport(report: ConformanceReport): Promise<void> {
|
|
502
|
+
const reportsDir = join(__dirname, "..", "reports");
|
|
503
|
+
|
|
504
|
+
if (!existsSync(reportsDir)) {
|
|
505
|
+
mkdirSync(reportsDir, { recursive: true });
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
509
|
+
const reportFile = join(reportsDir, `conformance-${timestamp}.json`);
|
|
510
|
+
|
|
511
|
+
writeFileSync(reportFile, JSON.stringify(report, null, 2));
|
|
512
|
+
console.log(chalk.green(`📄 Report saved to: ${reportFile}`));
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// CLI setup
|
|
517
|
+
const program = new Command();
|
|
518
|
+
|
|
519
|
+
program
|
|
520
|
+
.name("oap-conformance")
|
|
521
|
+
.description("Open Agent Passport conformance test runner")
|
|
522
|
+
.version("1.0.0");
|
|
523
|
+
|
|
524
|
+
program
|
|
525
|
+
.option("-p, --pack <pack>", "Run tests for specific policy pack")
|
|
526
|
+
.option("-v, --verbose", "Verbose output")
|
|
527
|
+
.option("-r, --report", "Generate detailed report")
|
|
528
|
+
.action(async (options: any) => {
|
|
529
|
+
const runner = new ConformanceRunner(options);
|
|
530
|
+
await runner.run();
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
program.parse();
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema validators for OAP conformance testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Ajv from "ajv";
|
|
6
|
+
import addFormats from "ajv-formats";
|
|
7
|
+
|
|
8
|
+
export class SchemaValidator {
|
|
9
|
+
private ajv: Ajv;
|
|
10
|
+
private passportSchema: any;
|
|
11
|
+
private decisionSchema: any;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.ajv = new Ajv({ allErrors: true });
|
|
15
|
+
addFormats(this.ajv);
|
|
16
|
+
// Use fallback schemas for now
|
|
17
|
+
this.passportSchema = this.getFallbackPassportSchema();
|
|
18
|
+
this.decisionSchema = this.getFallbackDecisionSchema();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async validatePassport(
|
|
22
|
+
passport: any
|
|
23
|
+
): Promise<{ valid: boolean; errors: string[] }> {
|
|
24
|
+
const validate = this.ajv.compile(this.passportSchema);
|
|
25
|
+
const valid = validate(passport);
|
|
26
|
+
|
|
27
|
+
if (!valid) {
|
|
28
|
+
const errors = validate.errors?.map(
|
|
29
|
+
(err) => `${err.instancePath || "root"}: ${err.message}`
|
|
30
|
+
) || ["Unknown validation error"];
|
|
31
|
+
return { valid: false, errors };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { valid: true, errors: [] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async validateDecision(
|
|
38
|
+
decision: any
|
|
39
|
+
): Promise<{ valid: boolean; errors: string[] }> {
|
|
40
|
+
const validate = this.ajv.compile(this.decisionSchema);
|
|
41
|
+
const valid = validate(decision);
|
|
42
|
+
|
|
43
|
+
if (!valid) {
|
|
44
|
+
const errors = validate.errors?.map(
|
|
45
|
+
(err) => `${err.instancePath || "root"}: ${err.message}`
|
|
46
|
+
) || ["Unknown validation error"];
|
|
47
|
+
return { valid: false, errors };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { valid: true, errors: [] };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async validateContext(
|
|
54
|
+
packId: string,
|
|
55
|
+
context: any
|
|
56
|
+
): Promise<{ valid: boolean; errors: string[] }> {
|
|
57
|
+
const errors: string[] = [];
|
|
58
|
+
|
|
59
|
+
// Basic context validation based on pack ID
|
|
60
|
+
switch (packId) {
|
|
61
|
+
case "finance.payment.refund.v1":
|
|
62
|
+
if (!context.amount || typeof context.amount !== "number") {
|
|
63
|
+
errors.push("amount is required and must be a number");
|
|
64
|
+
}
|
|
65
|
+
if (!context.currency || typeof context.currency !== "string") {
|
|
66
|
+
errors.push("currency is required and must be a string");
|
|
67
|
+
}
|
|
68
|
+
if (!context.order_id || typeof context.order_id !== "string") {
|
|
69
|
+
errors.push("order_id is required and must be a string");
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case "data.export.create.v1":
|
|
74
|
+
if (!context.collection || typeof context.collection !== "string") {
|
|
75
|
+
errors.push("collection is required and must be a string");
|
|
76
|
+
}
|
|
77
|
+
if (
|
|
78
|
+
context.estimated_rows &&
|
|
79
|
+
typeof context.estimated_rows !== "number"
|
|
80
|
+
) {
|
|
81
|
+
errors.push("estimated_rows must be a number");
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case "repo.release.publish.v1":
|
|
86
|
+
if (!context.repo || typeof context.repo !== "string") {
|
|
87
|
+
errors.push("repo is required and must be a string");
|
|
88
|
+
}
|
|
89
|
+
if (!context.branch || typeof context.branch !== "string") {
|
|
90
|
+
errors.push("branch is required and must be a string");
|
|
91
|
+
}
|
|
92
|
+
if (!context.tag || typeof context.tag !== "string") {
|
|
93
|
+
errors.push("tag is required and must be a string");
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
default:
|
|
98
|
+
errors.push(`Unknown policy pack: ${packId}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
valid: errors.length === 0,
|
|
103
|
+
errors,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private getFallbackPassportSchema(): any {
|
|
108
|
+
return {
|
|
109
|
+
type: "object",
|
|
110
|
+
required: [
|
|
111
|
+
"passport_id",
|
|
112
|
+
"kind",
|
|
113
|
+
"spec_version",
|
|
114
|
+
"owner_id",
|
|
115
|
+
"owner_type",
|
|
116
|
+
"status",
|
|
117
|
+
"assurance_level",
|
|
118
|
+
"capabilities",
|
|
119
|
+
"limits",
|
|
120
|
+
"regions",
|
|
121
|
+
"created_at",
|
|
122
|
+
"updated_at",
|
|
123
|
+
"version",
|
|
124
|
+
],
|
|
125
|
+
properties: {
|
|
126
|
+
passport_id: { type: "string", format: "uuid" },
|
|
127
|
+
kind: { type: "string", enum: ["template", "instance"] },
|
|
128
|
+
spec_version: { type: "string", const: "oap/1.0" },
|
|
129
|
+
owner_id: { type: "string" },
|
|
130
|
+
owner_type: { type: "string", enum: ["org", "user"] },
|
|
131
|
+
assurance_level: {
|
|
132
|
+
type: "string",
|
|
133
|
+
enum: ["L0", "L1", "L2", "L3", "L4KYC", "L4FIN"],
|
|
134
|
+
},
|
|
135
|
+
status: {
|
|
136
|
+
type: "string",
|
|
137
|
+
enum: ["draft", "active", "suspended", "revoked"],
|
|
138
|
+
},
|
|
139
|
+
capabilities: { type: "array" },
|
|
140
|
+
limits: { type: "object" },
|
|
141
|
+
regions: { type: "array" },
|
|
142
|
+
created_at: { type: "string", format: "date-time" },
|
|
143
|
+
updated_at: { type: "string", format: "date-time" },
|
|
144
|
+
version: { type: "string" },
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private getFallbackDecisionSchema(): any {
|
|
150
|
+
return {
|
|
151
|
+
type: "object",
|
|
152
|
+
required: [
|
|
153
|
+
"decision_id",
|
|
154
|
+
"policy_id",
|
|
155
|
+
"agent_id",
|
|
156
|
+
"owner_id",
|
|
157
|
+
"assurance_level",
|
|
158
|
+
"allow",
|
|
159
|
+
"reasons",
|
|
160
|
+
"created_at",
|
|
161
|
+
"expires_in",
|
|
162
|
+
"passport_digest",
|
|
163
|
+
"signature",
|
|
164
|
+
"kid",
|
|
165
|
+
],
|
|
166
|
+
properties: {
|
|
167
|
+
decision_id: { type: "string", format: "uuid" },
|
|
168
|
+
policy_id: { type: "string" },
|
|
169
|
+
agent_id: { type: "string", format: "uuid" },
|
|
170
|
+
owner_id: { type: "string" },
|
|
171
|
+
assurance_level: {
|
|
172
|
+
type: "string",
|
|
173
|
+
enum: ["L0", "L1", "L2", "L3", "L4KYC", "L4FIN"],
|
|
174
|
+
},
|
|
175
|
+
allow: { type: "boolean" },
|
|
176
|
+
reasons: { type: "array" },
|
|
177
|
+
created_at: { type: "string", format: "date-time" },
|
|
178
|
+
expires_in: { type: "integer", minimum: 0 },
|
|
179
|
+
passport_digest: { type: "string", pattern: "^sha256:[a-f0-9]{64}$" },
|
|
180
|
+
signature: { type: "string", pattern: "^ed25519:[A-Za-z0-9+/=]+$" },
|
|
181
|
+
kid: { type: "string" },
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|