@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,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple test script to verify the conformance runner works
|
|
5
|
+
* This is a fallback for when TypeScript compilation isn't available
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Mock implementations for testing
|
|
16
|
+
class MockValidator {
|
|
17
|
+
async validatePassport(passport) {
|
|
18
|
+
return { valid: true, errors: [] };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async validateDecision(decision) {
|
|
22
|
+
return { valid: true, errors: [] };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async validateContext(packId, context) {
|
|
26
|
+
return { valid: true, errors: [] };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class MockJCS {
|
|
31
|
+
canonicalize(obj) {
|
|
32
|
+
return JSON.stringify(obj, Object.keys(obj).sort());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async computePassportDigest(passport) {
|
|
36
|
+
return "sha256:test_digest";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class MockEd25519 {
|
|
41
|
+
async verify(message, signature, publicKey) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async verifyDecisionSignature(decision, signature, publicKey) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async verifyPassportDigest(passport, digest) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Simple test runner
|
|
55
|
+
class SimpleTestRunner {
|
|
56
|
+
constructor() {
|
|
57
|
+
this.validator = new MockValidator();
|
|
58
|
+
this.jcs = new MockJCS();
|
|
59
|
+
this.ed25519 = new MockEd25519();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async run() {
|
|
63
|
+
console.log("🔍 OAP Conformance Test Runner v1.0.0 (Simple Mode)\n");
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Load test cases
|
|
67
|
+
const testCases = await this.loadTestCases();
|
|
68
|
+
console.log(`✅ Loaded ${testCases.length} test cases`);
|
|
69
|
+
|
|
70
|
+
// Run tests
|
|
71
|
+
const results = await this.runTests(testCases);
|
|
72
|
+
|
|
73
|
+
// Display results
|
|
74
|
+
this.displayResults(results);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("❌ Test execution failed:", error.message);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async loadTestCases() {
|
|
82
|
+
const casesDir = path.join(__dirname, "cases");
|
|
83
|
+
const testCases = [];
|
|
84
|
+
|
|
85
|
+
if (!fs.existsSync(casesDir)) {
|
|
86
|
+
console.warn("⚠️ No test cases directory found");
|
|
87
|
+
return testCases;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const packDirs = fs
|
|
91
|
+
.readdirSync(casesDir, { withFileTypes: true })
|
|
92
|
+
.filter((dirent) => dirent.isDirectory())
|
|
93
|
+
.map((dirent) => path.join(casesDir, dirent.name));
|
|
94
|
+
|
|
95
|
+
for (const packDir of packDirs) {
|
|
96
|
+
const packId = path.basename(packDir);
|
|
97
|
+
const packCases = await this.loadPackTestCases(packDir, packId);
|
|
98
|
+
testCases.push(...packCases);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return testCases;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async loadPackTestCases(packDir, packId) {
|
|
105
|
+
const testCases = [];
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const passportsDir = path.join(packDir, "passports");
|
|
109
|
+
const contextsDir = path.join(packDir, "contexts");
|
|
110
|
+
const expectedDir = path.join(packDir, "expected");
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
!fs.existsSync(passportsDir) ||
|
|
114
|
+
!fs.existsSync(contextsDir) ||
|
|
115
|
+
!fs.existsSync(expectedDir)
|
|
116
|
+
) {
|
|
117
|
+
console.warn(`⚠️ Skipping ${packId}: missing required directories`);
|
|
118
|
+
return testCases;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const passportFiles = fs
|
|
122
|
+
.readdirSync(passportsDir)
|
|
123
|
+
.filter((f) => f.endsWith(".json"));
|
|
124
|
+
const contextFiles = fs
|
|
125
|
+
.readdirSync(contextsDir)
|
|
126
|
+
.filter((f) => f.endsWith(".json"));
|
|
127
|
+
const expectedFiles = fs
|
|
128
|
+
.readdirSync(expectedDir)
|
|
129
|
+
.filter((f) => f.endsWith(".json"));
|
|
130
|
+
|
|
131
|
+
for (const contextFile of contextFiles) {
|
|
132
|
+
const contextName = contextFile.replace(".json", "");
|
|
133
|
+
const expectedFile = expectedFiles.find((f) => f.includes(contextName));
|
|
134
|
+
|
|
135
|
+
if (!expectedFile) {
|
|
136
|
+
console.warn(`⚠️ No expected result for context: ${contextName}`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const testCase = {
|
|
141
|
+
id: `${packId}:${contextName}`,
|
|
142
|
+
packId,
|
|
143
|
+
contextName,
|
|
144
|
+
passport: JSON.parse(
|
|
145
|
+
fs.readFileSync(path.join(passportsDir, passportFiles[0]), "utf-8")
|
|
146
|
+
),
|
|
147
|
+
context: JSON.parse(
|
|
148
|
+
fs.readFileSync(path.join(contextsDir, contextFile), "utf-8")
|
|
149
|
+
),
|
|
150
|
+
expected: JSON.parse(
|
|
151
|
+
fs.readFileSync(path.join(expectedDir, expectedFile), "utf-8")
|
|
152
|
+
),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
testCases.push(testCase);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.warn(`⚠️ Error loading ${packId}: ${error.message}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return testCases;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async runTests(testCases) {
|
|
165
|
+
const results = [];
|
|
166
|
+
|
|
167
|
+
for (const testCase of testCases) {
|
|
168
|
+
console.log(`Running ${testCase.id}...`);
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
const result = await this.runTestCase(testCase);
|
|
172
|
+
results.push(result);
|
|
173
|
+
|
|
174
|
+
if (result.passed) {
|
|
175
|
+
console.log(` ✅ PASS`);
|
|
176
|
+
} else {
|
|
177
|
+
console.log(` ❌ FAIL: ${result.errors.join(", ")}`);
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log(` ❌ ERROR: ${error.message}`);
|
|
181
|
+
results.push({
|
|
182
|
+
testCase,
|
|
183
|
+
passed: false,
|
|
184
|
+
errors: [error.message],
|
|
185
|
+
warnings: [],
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return results;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async runTestCase(testCase) {
|
|
194
|
+
const errors = [];
|
|
195
|
+
|
|
196
|
+
// 1. Validate passport
|
|
197
|
+
const passportValidation = await this.validator.validatePassport(
|
|
198
|
+
testCase.passport
|
|
199
|
+
);
|
|
200
|
+
if (!passportValidation.valid) {
|
|
201
|
+
errors.push(
|
|
202
|
+
`Passport validation failed: ${passportValidation.errors.join(", ")}`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 2. Validate context
|
|
207
|
+
const contextValidation = await this.validator.validateContext(
|
|
208
|
+
testCase.packId,
|
|
209
|
+
testCase.context
|
|
210
|
+
);
|
|
211
|
+
if (!contextValidation.valid) {
|
|
212
|
+
errors.push(
|
|
213
|
+
`Context validation failed: ${contextValidation.errors.join(", ")}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 3. Validate expected decision
|
|
218
|
+
const decisionValidation = await this.validator.validateDecision(
|
|
219
|
+
testCase.expected
|
|
220
|
+
);
|
|
221
|
+
if (!decisionValidation.valid) {
|
|
222
|
+
errors.push(
|
|
223
|
+
`Expected decision validation failed: ${decisionValidation.errors.join(
|
|
224
|
+
", "
|
|
225
|
+
)}`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 4. Basic policy evaluation (simplified)
|
|
230
|
+
const policyResult = await this.evaluatePolicy(testCase);
|
|
231
|
+
if (!policyResult.valid) {
|
|
232
|
+
errors.push(
|
|
233
|
+
`Policy evaluation failed: ${policyResult.errors.join(", ")}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
testCase,
|
|
239
|
+
passed: errors.length === 0,
|
|
240
|
+
errors,
|
|
241
|
+
warnings: [],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async evaluatePolicy(testCase) {
|
|
246
|
+
// Simplified policy evaluation for testing
|
|
247
|
+
const { packId, passport, context } = testCase;
|
|
248
|
+
|
|
249
|
+
if (packId === "finance.payment.refund.v1") {
|
|
250
|
+
const amount = context.amount || 0;
|
|
251
|
+
const currency = context.currency || "USD";
|
|
252
|
+
|
|
253
|
+
const limits =
|
|
254
|
+
passport.limits?.["finance.payment.refund"]?.currency_limits?.[
|
|
255
|
+
currency
|
|
256
|
+
];
|
|
257
|
+
if (limits && amount > limits.max_per_tx) {
|
|
258
|
+
return {
|
|
259
|
+
valid: false,
|
|
260
|
+
errors: [
|
|
261
|
+
`Amount ${amount} exceeds max per transaction ${limits.max_per_tx}`,
|
|
262
|
+
],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (packId === "data.export.create.v1") {
|
|
268
|
+
const includePii = context.include_pii || false;
|
|
269
|
+
const allowPii = passport.limits?.["data.export"]?.allow_pii || false;
|
|
270
|
+
|
|
271
|
+
if (includePii && !allowPii) {
|
|
272
|
+
return {
|
|
273
|
+
valid: false,
|
|
274
|
+
errors: ["PII export not allowed"],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { valid: true, errors: [] };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
displayResults(results) {
|
|
283
|
+
console.log("\n📊 Test Results\n");
|
|
284
|
+
|
|
285
|
+
const passed = results.filter((r) => r.passed).length;
|
|
286
|
+
const failed = results.filter((r) => !r.passed).length;
|
|
287
|
+
const total = results.length;
|
|
288
|
+
|
|
289
|
+
console.log(`✅ Passed: ${passed}`);
|
|
290
|
+
console.log(`❌ Failed: ${failed}`);
|
|
291
|
+
console.log(
|
|
292
|
+
`📈 Success Rate: ${total > 0 ? ((passed / total) * 100).toFixed(1) : 0}%`
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
if (failed > 0) {
|
|
296
|
+
console.log("\n❌ Failed Tests:");
|
|
297
|
+
results
|
|
298
|
+
.filter((r) => !r.passed)
|
|
299
|
+
.forEach((result) => {
|
|
300
|
+
console.log(` • ${result.testCase.id}`);
|
|
301
|
+
result.errors.forEach((error) => {
|
|
302
|
+
console.log(` - ${error}`);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
console.log("\n🎯 Conformance testing complete!\n");
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Run the test
|
|
312
|
+
const runner = new SimpleTestRunner();
|
|
313
|
+
runner.run().catch(console.error);
|
|
314
|
+
|
|
315
|
+
export { SimpleTestRunner };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"allowSyntheticDefaultImports": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"outDir": "./dist",
|
|
14
|
+
"rootDir": "./src",
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"types": ["node"],
|
|
17
|
+
"lib": ["ES2022", "DOM"]
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*"],
|
|
20
|
+
"exclude": ["node_modules", "dist", "reports"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "OAP Error Response Schema",
|
|
4
|
+
"description": "Standardized error response format for Open Agent Passport API",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["error", "timestamp"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"error": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"required": ["code", "message"],
|
|
11
|
+
"properties": {
|
|
12
|
+
"code": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Machine-readable error code",
|
|
15
|
+
"enum": [
|
|
16
|
+
"validation_failed",
|
|
17
|
+
"unauthorized",
|
|
18
|
+
"forbidden",
|
|
19
|
+
"not_found",
|
|
20
|
+
"conflict",
|
|
21
|
+
"rate_limit_exceeded",
|
|
22
|
+
"internal_server_error",
|
|
23
|
+
"service_unavailable",
|
|
24
|
+
"invalid_passport",
|
|
25
|
+
"invalid_signature",
|
|
26
|
+
"timestamp_expired",
|
|
27
|
+
"nonce_reused",
|
|
28
|
+
"policy_denied",
|
|
29
|
+
"capability_insufficient",
|
|
30
|
+
"assurance_insufficient",
|
|
31
|
+
"limit_exceeded",
|
|
32
|
+
"region_restricted",
|
|
33
|
+
"passport_suspended",
|
|
34
|
+
"passport_revoked",
|
|
35
|
+
"idempotency_conflict",
|
|
36
|
+
"invalid_policy",
|
|
37
|
+
"passport_not_found",
|
|
38
|
+
"decision_expired",
|
|
39
|
+
"signature_verification_failed",
|
|
40
|
+
"transport_error"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"message": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "Human-readable error message"
|
|
46
|
+
},
|
|
47
|
+
"details": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"description": "Additional error details",
|
|
50
|
+
"additionalProperties": true
|
|
51
|
+
},
|
|
52
|
+
"field": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "Field that caused the validation error"
|
|
55
|
+
},
|
|
56
|
+
"value": {
|
|
57
|
+
"description": "Value that caused the validation error"
|
|
58
|
+
},
|
|
59
|
+
"suggestion": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "Suggested fix for the error"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"timestamp": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"format": "date-time",
|
|
68
|
+
"description": "ISO 8601 timestamp when the error occurred"
|
|
69
|
+
},
|
|
70
|
+
"request_id": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "Unique identifier for the request that caused the error"
|
|
73
|
+
},
|
|
74
|
+
"path": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "API path that caused the error"
|
|
77
|
+
},
|
|
78
|
+
"method": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"enum": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
|
81
|
+
"description": "HTTP method that caused the error"
|
|
82
|
+
},
|
|
83
|
+
"status_code": {
|
|
84
|
+
"type": "integer",
|
|
85
|
+
"minimum": 400,
|
|
86
|
+
"maximum": 599,
|
|
87
|
+
"description": "HTTP status code"
|
|
88
|
+
},
|
|
89
|
+
"retry_after": {
|
|
90
|
+
"type": "integer",
|
|
91
|
+
"minimum": 1,
|
|
92
|
+
"description": "Seconds to wait before retrying (for rate limit errors)"
|
|
93
|
+
},
|
|
94
|
+
"rate_limit_info": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"description": "Rate limiting information (for rate limit errors)",
|
|
97
|
+
"properties": {
|
|
98
|
+
"limit": {
|
|
99
|
+
"type": "integer",
|
|
100
|
+
"description": "Rate limit threshold"
|
|
101
|
+
},
|
|
102
|
+
"remaining": {
|
|
103
|
+
"type": "integer",
|
|
104
|
+
"description": "Remaining requests in current window"
|
|
105
|
+
},
|
|
106
|
+
"reset": {
|
|
107
|
+
"type": "integer",
|
|
108
|
+
"description": "Unix timestamp when rate limit resets"
|
|
109
|
+
},
|
|
110
|
+
"window": {
|
|
111
|
+
"type": "integer",
|
|
112
|
+
"description": "Rate limit window duration in seconds"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"documentation_url": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"format": "uri",
|
|
119
|
+
"description": "URL to relevant documentation"
|
|
120
|
+
},
|
|
121
|
+
"support_url": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"format": "uri",
|
|
124
|
+
"description": "URL to support or help"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"examples": [
|
|
128
|
+
{
|
|
129
|
+
"error": {
|
|
130
|
+
"code": "validation_failed",
|
|
131
|
+
"message": "Name is required and must be between 1-100 characters",
|
|
132
|
+
"field": "name",
|
|
133
|
+
"value": "",
|
|
134
|
+
"suggestion": "Provide a valid agent name between 1 and 100 characters"
|
|
135
|
+
},
|
|
136
|
+
"timestamp": "2025-01-16T10:30:00Z",
|
|
137
|
+
"request_id": "req_123456789",
|
|
138
|
+
"path": "/api/passports",
|
|
139
|
+
"method": "POST",
|
|
140
|
+
"status_code": 400
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"error": {
|
|
144
|
+
"code": "rate_limit_exceeded",
|
|
145
|
+
"message": "Too many requests, please try again later"
|
|
146
|
+
},
|
|
147
|
+
"timestamp": "2025-01-16T10:30:00Z",
|
|
148
|
+
"request_id": "req_123456790",
|
|
149
|
+
"path": "/api/verify/ap_a2d10232c6534523812423eec8a1425c",
|
|
150
|
+
"method": "GET",
|
|
151
|
+
"status_code": 429,
|
|
152
|
+
"retry_after": 3600,
|
|
153
|
+
"rate_limit_info": {
|
|
154
|
+
"limit": 1000,
|
|
155
|
+
"remaining": 0,
|
|
156
|
+
"reset": 1640998800,
|
|
157
|
+
"window": 3600
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"error": {
|
|
162
|
+
"code": "passport_not_found",
|
|
163
|
+
"message": "Passport with ID ap_a2d10232c6534523812423eec8a1425c not found",
|
|
164
|
+
"details": {
|
|
165
|
+
"passport_id": "ap_a2d10232c6534523812423eec8a1425c",
|
|
166
|
+
"suggestion": "Verify the passport ID is correct and the passport exists"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"timestamp": "2025-01-16T10:30:00Z",
|
|
170
|
+
"request_id": "req_123456791",
|
|
171
|
+
"path": "/api/verify/ap_a2d10232c6534523812423eec8a1425c",
|
|
172
|
+
"method": "GET",
|
|
173
|
+
"status_code": 404
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"error": {
|
|
177
|
+
"code": "policy_denied",
|
|
178
|
+
"message": "Agent lacks required capabilities for this policy",
|
|
179
|
+
"details": {
|
|
180
|
+
"policy_id": "finance.payment.refund.v1",
|
|
181
|
+
"required_capabilities": ["finance.payment.refund"],
|
|
182
|
+
"agent_capabilities": ["data.export"]
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"timestamp": "2025-01-16T10:30:00Z",
|
|
186
|
+
"request_id": "req_123456792",
|
|
187
|
+
"path": "/api/verify/policy/finance.payment.refund.v1",
|
|
188
|
+
"method": "POST",
|
|
189
|
+
"status_code": 403
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"published_at": "2025-09-24T23:03:21Z",
|
|
4
|
+
"source_commit": "07d00d6d3e6a94367b40192a7841a27074c7bf6f",
|
|
5
|
+
"specifications": [
|
|
6
|
+
{"name": "passport-schema", "path": "passport-schema.json", "description": "APort passport schema definition"},
|
|
7
|
+
{"name": "error-schema", "path": "error-schema.json", "description": "APort error schema definition"},
|
|
8
|
+
{"name": "rate-limiting", "path": "rate-limiting.md", "description": "Rate limiting specification"},
|
|
9
|
+
{"name": "transport-profile", "path": "transport-profile.md", "description": "Transport profile specification"},
|
|
10
|
+
{"name": "webhook-spec", "path": "webhook-spec.md", "description": "Webhook specification"}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# ClawMoat Integration (Placeholder)
|
|
2
|
+
|
|
3
|
+
_Last updated: 2026-02-17_
|
|
4
|
+
|
|
5
|
+
ClawMoat is an open-source detection layer for AI agents (prompt injection, data exfiltration, secret leakage). In the layered model from OpenClaw issue #12385, ClawMoat supplies detections while OAP/APort performs authorization.
|
|
6
|
+
|
|
7
|
+
**Open Agent Passport (OAP) v1.0** ([spec](../../oap/oap-spec.md)) provides the runtime authorization layer SHIELD v1 envisions:
|
|
8
|
+
- Passports (W3C VC/DID-aligned) describe agent capabilities and limits.
|
|
9
|
+
- Policy packs define evaluation rules; **limits** (e.g. allowlists, blocked patterns, threat data) live in the **passport** under `limits.{capability}`.
|
|
10
|
+
- APort guardrails evaluate policy packs against passport + context and enforce deterministically (e.g. `before_tool_call` in OpenClaw).
|
|
11
|
+
|
|
12
|
+
This folder will hold the detector schema + adapter notes once we formalize the shared findings format. For now, the priority is shipping the SHIELD→OAP mapping; ClawMoat integration will follow the same `{threat_id, module, severity, confidence}` structure when we’re ready to onboard additional feeds.
|