@kya-os/mcp-i-core 1.3.13 → 1.3.15

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.
Files changed (255) hide show
  1. package/dist/config/remote-config.js +9 -12
  2. package/dist/runtime/base.d.ts +2 -1
  3. package/dist/runtime/base.js +34 -6
  4. package/dist/services/access-control.service.js +5 -0
  5. package/dist/services/tool-protection.service.js +17 -8
  6. package/package.json +2 -2
  7. package/.turbo/turbo-build.log +0 -4
  8. package/.turbo/turbo-test$colon$coverage.log +0 -4586
  9. package/.turbo/turbo-test.log +0 -4631
  10. package/COMPLIANCE_IMPROVEMENT_REPORT.md +0 -483
  11. package/Composer 3.md +0 -615
  12. package/GPT-5.md +0 -1169
  13. package/OPUS-plan.md +0 -352
  14. package/PHASE_3_AND_4.1_SUMMARY.md +0 -585
  15. package/PHASE_3_SUMMARY.md +0 -317
  16. package/PHASE_4.1.3_SUMMARY.md +0 -428
  17. package/PHASE_4.1_COMPLETE.md +0 -525
  18. package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +0 -1240
  19. package/SCHEMA_COMPLIANCE_REPORT.md +0 -275
  20. package/TEST_PLAN.md +0 -571
  21. package/coverage/coverage-final.json +0 -60
  22. package/dist/cache/oauth-config-cache.d.ts.map +0 -1
  23. package/dist/cache/oauth-config-cache.js.map +0 -1
  24. package/dist/cache/tool-protection-cache.d.ts.map +0 -1
  25. package/dist/cache/tool-protection-cache.js.map +0 -1
  26. package/dist/compliance/index.d.ts.map +0 -1
  27. package/dist/compliance/index.js.map +0 -1
  28. package/dist/compliance/schema-registry.d.ts.map +0 -1
  29. package/dist/compliance/schema-registry.js.map +0 -1
  30. package/dist/compliance/schema-verifier.d.ts.map +0 -1
  31. package/dist/compliance/schema-verifier.js.map +0 -1
  32. package/dist/config/remote-config.d.ts.map +0 -1
  33. package/dist/config/remote-config.js.map +0 -1
  34. package/dist/config.d.ts.map +0 -1
  35. package/dist/config.js.map +0 -1
  36. package/dist/delegation/audience-validator.d.ts.map +0 -1
  37. package/dist/delegation/audience-validator.js.map +0 -1
  38. package/dist/delegation/bitstring.d.ts.map +0 -1
  39. package/dist/delegation/bitstring.js.map +0 -1
  40. package/dist/delegation/cascading-revocation.d.ts.map +0 -1
  41. package/dist/delegation/cascading-revocation.js.map +0 -1
  42. package/dist/delegation/delegation-graph.d.ts.map +0 -1
  43. package/dist/delegation/delegation-graph.js.map +0 -1
  44. package/dist/delegation/did-key-resolver.d.ts.map +0 -1
  45. package/dist/delegation/did-key-resolver.js.map +0 -1
  46. package/dist/delegation/index.d.ts.map +0 -1
  47. package/dist/delegation/index.js.map +0 -1
  48. package/dist/delegation/statuslist-manager.d.ts.map +0 -1
  49. package/dist/delegation/statuslist-manager.js.map +0 -1
  50. package/dist/delegation/storage/index.d.ts.map +0 -1
  51. package/dist/delegation/storage/index.js.map +0 -1
  52. package/dist/delegation/storage/memory-graph-storage.d.ts.map +0 -1
  53. package/dist/delegation/storage/memory-graph-storage.js.map +0 -1
  54. package/dist/delegation/storage/memory-statuslist-storage.d.ts.map +0 -1
  55. package/dist/delegation/storage/memory-statuslist-storage.js.map +0 -1
  56. package/dist/delegation/utils.d.ts.map +0 -1
  57. package/dist/delegation/utils.js.map +0 -1
  58. package/dist/delegation/vc-issuer.d.ts.map +0 -1
  59. package/dist/delegation/vc-issuer.js.map +0 -1
  60. package/dist/delegation/vc-verifier.d.ts.map +0 -1
  61. package/dist/delegation/vc-verifier.js.map +0 -1
  62. package/dist/identity/idp-token-resolver.d.ts.map +0 -1
  63. package/dist/identity/idp-token-resolver.js.map +0 -1
  64. package/dist/identity/idp-token-storage.interface.d.ts.map +0 -1
  65. package/dist/identity/idp-token-storage.interface.js.map +0 -1
  66. package/dist/identity/user-did-manager.d.ts.map +0 -1
  67. package/dist/identity/user-did-manager.js.map +0 -1
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/index.js.map +0 -1
  70. package/dist/providers/base.d.ts.map +0 -1
  71. package/dist/providers/base.js.map +0 -1
  72. package/dist/providers/memory.d.ts.map +0 -1
  73. package/dist/providers/memory.js.map +0 -1
  74. package/dist/runtime/audit-logger.d.ts.map +0 -1
  75. package/dist/runtime/audit-logger.js.map +0 -1
  76. package/dist/runtime/base.d.ts.map +0 -1
  77. package/dist/runtime/base.js.map +0 -1
  78. package/dist/services/access-control.service.d.ts.map +0 -1
  79. package/dist/services/access-control.service.js.map +0 -1
  80. package/dist/services/authorization/authorization-registry.d.ts.map +0 -1
  81. package/dist/services/authorization/authorization-registry.js.map +0 -1
  82. package/dist/services/authorization/types.d.ts.map +0 -1
  83. package/dist/services/authorization/types.js.map +0 -1
  84. package/dist/services/batch-delegation.service.d.ts.map +0 -1
  85. package/dist/services/batch-delegation.service.js.map +0 -1
  86. package/dist/services/crypto.service.d.ts.map +0 -1
  87. package/dist/services/crypto.service.js.map +0 -1
  88. package/dist/services/errors.d.ts.map +0 -1
  89. package/dist/services/errors.js.map +0 -1
  90. package/dist/services/index.d.ts.map +0 -1
  91. package/dist/services/index.js.map +0 -1
  92. package/dist/services/oauth-config.service.d.ts.map +0 -1
  93. package/dist/services/oauth-config.service.js.map +0 -1
  94. package/dist/services/oauth-provider-registry.d.ts.map +0 -1
  95. package/dist/services/oauth-provider-registry.js.map +0 -1
  96. package/dist/services/oauth-service.d.ts.map +0 -1
  97. package/dist/services/oauth-service.js.map +0 -1
  98. package/dist/services/oauth-token-retrieval.service.d.ts.map +0 -1
  99. package/dist/services/oauth-token-retrieval.service.js.map +0 -1
  100. package/dist/services/proof-verifier.d.ts.map +0 -1
  101. package/dist/services/proof-verifier.js.map +0 -1
  102. package/dist/services/provider-resolver.d.ts.map +0 -1
  103. package/dist/services/provider-resolver.js.map +0 -1
  104. package/dist/services/provider-validator.d.ts.map +0 -1
  105. package/dist/services/provider-validator.js.map +0 -1
  106. package/dist/services/session-registration.service.d.ts.map +0 -1
  107. package/dist/services/session-registration.service.js.map +0 -1
  108. package/dist/services/storage.service.d.ts.map +0 -1
  109. package/dist/services/storage.service.js.map +0 -1
  110. package/dist/services/tool-context-builder.d.ts.map +0 -1
  111. package/dist/services/tool-context-builder.js.map +0 -1
  112. package/dist/services/tool-protection.service.d.ts.map +0 -1
  113. package/dist/services/tool-protection.service.js.map +0 -1
  114. package/dist/types/oauth-required-error.d.ts.map +0 -1
  115. package/dist/types/oauth-required-error.js.map +0 -1
  116. package/dist/types/tool-protection.d.ts.map +0 -1
  117. package/dist/types/tool-protection.js.map +0 -1
  118. package/dist/utils/base58.d.ts.map +0 -1
  119. package/dist/utils/base58.js.map +0 -1
  120. package/dist/utils/base64.d.ts.map +0 -1
  121. package/dist/utils/base64.js.map +0 -1
  122. package/dist/utils/cors.d.ts.map +0 -1
  123. package/dist/utils/cors.js.map +0 -1
  124. package/dist/utils/did-helpers.d.ts.map +0 -1
  125. package/dist/utils/did-helpers.js.map +0 -1
  126. package/dist/utils/index.d.ts.map +0 -1
  127. package/dist/utils/index.js.map +0 -1
  128. package/dist/utils/storage-keys.d.ts.map +0 -1
  129. package/dist/utils/storage-keys.js.map +0 -1
  130. package/docs/API_REFERENCE.md +0 -1362
  131. package/docs/COMPLIANCE_MATRIX.md +0 -691
  132. package/docs/STATUSLIST2021_GUIDE.md +0 -696
  133. package/docs/W3C_VC_DELEGATION_GUIDE.md +0 -710
  134. package/src/__tests__/cache/tool-protection-cache.test.ts +0 -640
  135. package/src/__tests__/config/provider-runtime-config.test.ts +0 -309
  136. package/src/__tests__/delegation-e2e.test.ts +0 -690
  137. package/src/__tests__/identity/user-did-manager.test.ts +0 -232
  138. package/src/__tests__/index.test.ts +0 -56
  139. package/src/__tests__/integration/full-flow.test.ts +0 -789
  140. package/src/__tests__/integration.test.ts +0 -281
  141. package/src/__tests__/providers/base.test.ts +0 -173
  142. package/src/__tests__/providers/memory.test.ts +0 -319
  143. package/src/__tests__/regression/phase2-regression.test.ts +0 -429
  144. package/src/__tests__/runtime/audit-logger.test.ts +0 -154
  145. package/src/__tests__/runtime/base-extensions.test.ts +0 -595
  146. package/src/__tests__/runtime/base.test.ts +0 -869
  147. package/src/__tests__/runtime/delegation-flow.test.ts +0 -164
  148. package/src/__tests__/runtime/proof-client-did.test.ts +0 -376
  149. package/src/__tests__/runtime/route-interception.test.ts +0 -686
  150. package/src/__tests__/runtime/tool-protection-enforcement.test.ts +0 -908
  151. package/src/__tests__/services/agentshield-integration.test.ts +0 -791
  152. package/src/__tests__/services/cache-busting.test.ts +0 -125
  153. package/src/__tests__/services/oauth-service-pkce.test.ts +0 -556
  154. package/src/__tests__/services/provider-resolver-edge-cases.test.ts +0 -591
  155. package/src/__tests__/services/tool-protection-merged-config.test.ts +0 -485
  156. package/src/__tests__/services/tool-protection-oauth-provider.test.ts +0 -480
  157. package/src/__tests__/services/tool-protection.service.test.ts +0 -1373
  158. package/src/__tests__/utils/mock-providers.ts +0 -340
  159. package/src/cache/oauth-config-cache.d.ts +0 -69
  160. package/src/cache/oauth-config-cache.d.ts.map +0 -1
  161. package/src/cache/oauth-config-cache.js.map +0 -1
  162. package/src/cache/oauth-config-cache.ts +0 -123
  163. package/src/cache/tool-protection-cache.ts +0 -171
  164. package/src/compliance/EXAMPLE.md +0 -412
  165. package/src/compliance/__tests__/schema-verifier.test.ts +0 -797
  166. package/src/compliance/index.ts +0 -8
  167. package/src/compliance/schema-registry.ts +0 -460
  168. package/src/compliance/schema-verifier.ts +0 -708
  169. package/src/config/__tests__/merged-config.spec.ts +0 -445
  170. package/src/config/__tests__/remote-config.spec.ts +0 -268
  171. package/src/config/remote-config.ts +0 -264
  172. package/src/config.ts +0 -312
  173. package/src/delegation/__tests__/audience-validator.test.ts +0 -112
  174. package/src/delegation/__tests__/bitstring.test.ts +0 -346
  175. package/src/delegation/__tests__/cascading-revocation.test.ts +0 -628
  176. package/src/delegation/__tests__/delegation-graph.test.ts +0 -584
  177. package/src/delegation/__tests__/did-key-resolver.test.ts +0 -265
  178. package/src/delegation/__tests__/utils.test.ts +0 -152
  179. package/src/delegation/__tests__/vc-issuer.test.ts +0 -442
  180. package/src/delegation/__tests__/vc-verifier.test.ts +0 -922
  181. package/src/delegation/audience-validator.ts +0 -52
  182. package/src/delegation/bitstring.ts +0 -278
  183. package/src/delegation/cascading-revocation.ts +0 -370
  184. package/src/delegation/delegation-graph.ts +0 -299
  185. package/src/delegation/did-key-resolver.ts +0 -179
  186. package/src/delegation/index.ts +0 -14
  187. package/src/delegation/statuslist-manager.ts +0 -353
  188. package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +0 -366
  189. package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +0 -228
  190. package/src/delegation/storage/index.ts +0 -9
  191. package/src/delegation/storage/memory-graph-storage.ts +0 -178
  192. package/src/delegation/storage/memory-statuslist-storage.ts +0 -77
  193. package/src/delegation/utils.ts +0 -221
  194. package/src/delegation/vc-issuer.ts +0 -232
  195. package/src/delegation/vc-verifier.ts +0 -568
  196. package/src/identity/idp-token-resolver.ts +0 -181
  197. package/src/identity/idp-token-storage.interface.ts +0 -94
  198. package/src/identity/user-did-manager.ts +0 -526
  199. package/src/index.ts +0 -310
  200. package/src/providers/base.d.ts +0 -91
  201. package/src/providers/base.d.ts.map +0 -1
  202. package/src/providers/base.js.map +0 -1
  203. package/src/providers/base.ts +0 -96
  204. package/src/providers/memory.ts +0 -142
  205. package/src/runtime/audit-logger.ts +0 -39
  206. package/src/runtime/base.ts +0 -1392
  207. package/src/services/__tests__/access-control.integration.test.ts +0 -443
  208. package/src/services/__tests__/access-control.proof-response-validation.test.ts +0 -578
  209. package/src/services/__tests__/access-control.service.test.ts +0 -970
  210. package/src/services/__tests__/batch-delegation.service.test.ts +0 -351
  211. package/src/services/__tests__/crypto.service.test.ts +0 -531
  212. package/src/services/__tests__/oauth-provider-registry.test.ts +0 -142
  213. package/src/services/__tests__/proof-verifier.integration.test.ts +0 -485
  214. package/src/services/__tests__/proof-verifier.test.ts +0 -489
  215. package/src/services/__tests__/provider-resolution.integration.test.ts +0 -202
  216. package/src/services/__tests__/provider-resolver.test.ts +0 -213
  217. package/src/services/__tests__/storage.service.test.ts +0 -358
  218. package/src/services/access-control.service.ts +0 -990
  219. package/src/services/authorization/authorization-registry.ts +0 -66
  220. package/src/services/authorization/types.ts +0 -71
  221. package/src/services/batch-delegation.service.ts +0 -137
  222. package/src/services/crypto.service.ts +0 -302
  223. package/src/services/errors.ts +0 -76
  224. package/src/services/index.ts +0 -18
  225. package/src/services/oauth-config.service.d.ts +0 -53
  226. package/src/services/oauth-config.service.d.ts.map +0 -1
  227. package/src/services/oauth-config.service.js.map +0 -1
  228. package/src/services/oauth-config.service.ts +0 -192
  229. package/src/services/oauth-provider-registry.d.ts +0 -57
  230. package/src/services/oauth-provider-registry.d.ts.map +0 -1
  231. package/src/services/oauth-provider-registry.js.map +0 -1
  232. package/src/services/oauth-provider-registry.ts +0 -141
  233. package/src/services/oauth-service.ts +0 -544
  234. package/src/services/oauth-token-retrieval.service.ts +0 -245
  235. package/src/services/proof-verifier.ts +0 -478
  236. package/src/services/provider-resolver.d.ts +0 -48
  237. package/src/services/provider-resolver.d.ts.map +0 -1
  238. package/src/services/provider-resolver.js.map +0 -1
  239. package/src/services/provider-resolver.ts +0 -146
  240. package/src/services/provider-validator.ts +0 -170
  241. package/src/services/session-registration.service.ts +0 -251
  242. package/src/services/storage.service.ts +0 -566
  243. package/src/services/tool-context-builder.ts +0 -237
  244. package/src/services/tool-protection.service.ts +0 -1070
  245. package/src/types/oauth-required-error.ts +0 -63
  246. package/src/types/tool-protection.ts +0 -155
  247. package/src/utils/__tests__/did-helpers.test.ts +0 -156
  248. package/src/utils/base58.ts +0 -109
  249. package/src/utils/base64.ts +0 -148
  250. package/src/utils/cors.ts +0 -83
  251. package/src/utils/did-helpers.ts +0 -210
  252. package/src/utils/index.ts +0 -8
  253. package/src/utils/storage-keys.ts +0 -278
  254. package/tsconfig.json +0 -21
  255. package/vitest.config.ts +0 -56
@@ -1,908 +0,0 @@
1
- /**
2
- * Tool Protection Enforcement Tests
3
- *
4
- * Tests for tool protection enforcement in processToolCall.
5
- */
6
-
7
- import { describe, it, expect, beforeEach, vi } from "vitest";
8
- import { MCPIRuntimeBase } from "../../runtime/base";
9
- import { ProviderRuntimeConfig } from "../../config";
10
- import {
11
- createMockProviders,
12
- MockClockProvider,
13
- } from "../utils/mock-providers";
14
- import { ToolProtectionService } from "../../services/tool-protection.service";
15
- import { DelegationRequiredError } from "../../types/tool-protection";
16
- import { AccessControlApiService } from "../../services/access-control.service";
17
- import type { VerifyDelegationAPIResponse } from "@kya-os/contracts/agentshield-api";
18
-
19
- describe("MCPIRuntimeBase - Tool Protection Enforcement", () => {
20
- let runtime: MCPIRuntimeBase;
21
- let config: ProviderRuntimeConfig;
22
- let mockProviders: ReturnType<typeof createMockProviders>;
23
- let mockToolProtectionService: {
24
- checkToolProtection: ReturnType<typeof vi.fn>;
25
- };
26
- let mockAccessControlService: {
27
- verifyDelegation: ReturnType<typeof vi.fn>;
28
- };
29
-
30
- beforeEach(async () => {
31
- vi.clearAllMocks();
32
- mockProviders = createMockProviders();
33
-
34
- // Create mock tool protection service
35
- mockToolProtectionService = {
36
- checkToolProtection: vi.fn(),
37
- };
38
-
39
- // Create mock access control service
40
- mockAccessControlService = {
41
- verifyDelegation: vi.fn(),
42
- };
43
-
44
- config = {
45
- ...mockProviders,
46
- environment: "development",
47
- session: {
48
- timestampSkewSeconds: 120,
49
- ttlMinutes: 30,
50
- },
51
- audit: {
52
- enabled: true,
53
- logFunction: vi.fn(),
54
- includePayloads: true,
55
- },
56
- toolProtectionService: mockToolProtectionService as any,
57
- };
58
- runtime = new MCPIRuntimeBase(config);
59
- // Inject mock access control service
60
- (runtime as any).accessControlService = mockAccessControlService as any;
61
- await runtime.initialize();
62
- });
63
-
64
- describe("processToolCall with tool protection", () => {
65
- const mockHandler = vi.fn().mockResolvedValue({ result: "success" });
66
- const session = {
67
- id: "session123",
68
- audience: "https://client.example.com",
69
- nonce: "test-nonce",
70
- };
71
-
72
- it("should allow tool execution when no protection required", async () => {
73
- mockToolProtectionService.checkToolProtection.mockResolvedValue(null);
74
-
75
- const result = await runtime.processToolCall(
76
- "unprotectedTool",
77
- { arg: "value" },
78
- mockHandler,
79
- session
80
- );
81
-
82
- expect(mockHandler).toHaveBeenCalledWith({ arg: "value" });
83
- expect(result).toEqual({ result: "success" });
84
- expect(
85
- mockToolProtectionService.checkToolProtection
86
- ).toHaveBeenCalledWith("unprotectedTool", "did:key:zmock123");
87
- });
88
-
89
- it("should block tool execution when protection required and no delegation", async () => {
90
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
91
- requiresDelegation: true,
92
- requiredScopes: ["files:write"],
93
- });
94
-
95
- await expect(
96
- runtime.processToolCall(
97
- "protectedTool",
98
- { arg: "value" },
99
- mockHandler,
100
- session
101
- )
102
- ).rejects.toThrow(DelegationRequiredError);
103
-
104
- expect(mockHandler).not.toHaveBeenCalled();
105
- });
106
-
107
- it("should allow tool execution when protection required and delegation provided", async () => {
108
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
109
- requiresDelegation: true,
110
- requiredScopes: ["files:write"],
111
- });
112
-
113
- // Mock successful delegation verification
114
- mockAccessControlService.verifyDelegation.mockResolvedValue({
115
- success: true,
116
- data: {
117
- valid: true,
118
- delegation_id: "test-delegation-id",
119
- credential: {
120
- agent_did: "did:key:zmock123",
121
- scopes: ["files:write"],
122
- issued_at: Date.now(),
123
- created_at: Date.now(),
124
- },
125
- },
126
- } as VerifyDelegationAPIResponse);
127
-
128
- const sessionWithDelegation = {
129
- ...session,
130
- delegationToken: "valid-delegation-token",
131
- };
132
-
133
- const result = await runtime.processToolCall(
134
- "protectedTool",
135
- { arg: "value" },
136
- mockHandler,
137
- sessionWithDelegation
138
- );
139
-
140
- // Verify delegation was checked
141
- expect(mockAccessControlService.verifyDelegation).toHaveBeenCalledWith(
142
- expect.objectContaining({
143
- agent_did: "did:key:zmock123",
144
- delegation_token: "valid-delegation-token",
145
- scopes: ["files:write"],
146
- }),
147
- expect.objectContaining({
148
- delegationToken: "valid-delegation-token",
149
- })
150
- );
151
-
152
- expect(mockHandler).toHaveBeenCalledWith({ arg: "value" });
153
- expect(result).toEqual({ result: "success" });
154
- });
155
-
156
- it("should allow tool execution when protection required and consentProof provided", async () => {
157
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
158
- requiresDelegation: true,
159
- requiredScopes: ["files:write"],
160
- });
161
-
162
- // Mock successful delegation verification using consent proof as credential_jwt
163
- mockAccessControlService.verifyDelegation.mockResolvedValue({
164
- success: true,
165
- data: {
166
- valid: true,
167
- delegation_id: "test-delegation-id",
168
- credential: {
169
- agent_did: "did:key:zmock123",
170
- scopes: ["files:write"],
171
- issued_at: Date.now(),
172
- created_at: Date.now(),
173
- },
174
- },
175
- } as VerifyDelegationAPIResponse);
176
-
177
- const sessionWithConsent = {
178
- ...session,
179
- consentProof: "valid-consent-proof",
180
- };
181
-
182
- const result = await runtime.processToolCall(
183
- "protectedTool",
184
- { arg: "value" },
185
- mockHandler,
186
- sessionWithConsent
187
- );
188
-
189
- // Verify delegation was checked using consent proof
190
- expect(mockAccessControlService.verifyDelegation).toHaveBeenCalledWith(
191
- expect.objectContaining({
192
- agent_did: "did:key:zmock123",
193
- credential_jwt: "valid-consent-proof",
194
- scopes: ["files:write"],
195
- }),
196
- expect.objectContaining({
197
- credentialJwt: "valid-consent-proof",
198
- })
199
- );
200
-
201
- expect(mockHandler).toHaveBeenCalledWith({ arg: "value" });
202
- expect(result).toEqual({ result: "success" });
203
- });
204
-
205
- it("should block tool execution when delegation verification fails", async () => {
206
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
207
- requiresDelegation: true,
208
- requiredScopes: ["files:write"],
209
- });
210
-
211
- // Mock failed delegation verification
212
- mockAccessControlService.verifyDelegation.mockResolvedValue({
213
- success: true,
214
- data: {
215
- valid: false,
216
- reason: "Delegation token expired",
217
- },
218
- } as VerifyDelegationAPIResponse);
219
-
220
- const sessionWithDelegation = {
221
- ...session,
222
- delegationToken: "expired-delegation-token",
223
- };
224
-
225
- await expect(
226
- runtime.processToolCall(
227
- "protectedTool",
228
- { arg: "value" },
229
- mockHandler,
230
- sessionWithDelegation
231
- )
232
- ).rejects.toThrow(DelegationRequiredError);
233
-
234
- expect(mockAccessControlService.verifyDelegation).toHaveBeenCalled();
235
- expect(mockHandler).not.toHaveBeenCalled();
236
- });
237
-
238
- it("should block tool execution when delegation has wrong scopes", async () => {
239
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
240
- requiresDelegation: true,
241
- requiredScopes: ["files:write"],
242
- });
243
-
244
- // Mock delegation with insufficient scopes
245
- mockAccessControlService.verifyDelegation.mockResolvedValue({
246
- success: true,
247
- data: {
248
- valid: false,
249
- reason: "Insufficient scopes",
250
- credential: {
251
- agent_did: "did:key:zmock123",
252
- scopes: ["files:read"], // Only read, not write
253
- issued_at: Date.now(),
254
- created_at: Date.now(),
255
- },
256
- },
257
- } as VerifyDelegationAPIResponse);
258
-
259
- const sessionWithDelegation = {
260
- ...session,
261
- delegationToken: "insufficient-scope-token",
262
- };
263
-
264
- await expect(
265
- runtime.processToolCall(
266
- "protectedTool",
267
- { arg: "value" },
268
- mockHandler,
269
- sessionWithDelegation
270
- )
271
- ).rejects.toThrow(DelegationRequiredError);
272
-
273
- expect(mockAccessControlService.verifyDelegation).toHaveBeenCalledWith(
274
- expect.objectContaining({
275
- scopes: ["files:write"],
276
- }),
277
- expect.any(Object)
278
- );
279
- expect(mockHandler).not.toHaveBeenCalled();
280
- });
281
-
282
- it("should handle API errors during verification gracefully", async () => {
283
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
284
- requiresDelegation: true,
285
- requiredScopes: ["files:write"],
286
- });
287
-
288
- // Mock API error
289
- const { AgentShieldAPIError } = await import(
290
- "@kya-os/contracts/agentshield-api"
291
- );
292
- mockAccessControlService.verifyDelegation.mockRejectedValue(
293
- new AgentShieldAPIError("network_error", "API unavailable", {})
294
- );
295
-
296
- const sessionWithDelegation = {
297
- ...session,
298
- delegationToken: "valid-token",
299
- };
300
-
301
- // Should fail securely by requiring delegation
302
- await expect(
303
- runtime.processToolCall(
304
- "protectedTool",
305
- { arg: "value" },
306
- mockHandler,
307
- sessionWithDelegation
308
- )
309
- ).rejects.toThrow(DelegationRequiredError);
310
-
311
- expect(mockHandler).not.toHaveBeenCalled();
312
- });
313
-
314
- it("should allow tool execution when access control service not configured (graceful degradation)", async () => {
315
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
316
- requiresDelegation: true,
317
- requiredScopes: ["files:write"],
318
- });
319
-
320
- // Remove access control service
321
- (runtime as any).accessControlService = undefined;
322
-
323
- const sessionWithDelegation = {
324
- ...session,
325
- delegationToken: "valid-delegation-token",
326
- };
327
-
328
- // Should allow execution with warning (graceful degradation)
329
- const result = await runtime.processToolCall(
330
- "protectedTool",
331
- { arg: "value" },
332
- mockHandler,
333
- sessionWithDelegation
334
- );
335
-
336
- expect(mockHandler).toHaveBeenCalledWith({ arg: "value" });
337
- expect(result).toEqual({ result: "success" });
338
- });
339
-
340
- describe("user_identifier validation", () => {
341
- const userDidA = "did:key:zUserA123456789";
342
- const userDidB = "did:key:zUserB987654321";
343
-
344
- it("should reject delegation when user_identifier does not match session userDid", async () => {
345
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
346
- requiresDelegation: true,
347
- requiredScopes: ["files:write"],
348
- });
349
-
350
- // Mock verification response with User B's user_identifier
351
- mockAccessControlService.verifyDelegation.mockResolvedValue({
352
- success: true,
353
- data: {
354
- valid: true,
355
- delegation_id: "test-delegation-id",
356
- credential: {
357
- agent_did: "did:key:zmock123",
358
- user_identifier: userDidB, // ❌ User B's identifier, not User A's
359
- scopes: ["files:write"],
360
- issued_at: Date.now(),
361
- created_at: Date.now(),
362
- },
363
- },
364
- } as VerifyDelegationAPIResponse);
365
-
366
- // Session with User A's DID
367
- const sessionWithUserA = {
368
- ...session,
369
- userDid: userDidA, // User A's DID
370
- delegationToken: "delegation-token-for-user-b",
371
- };
372
-
373
- // Should reject delegation due to user_identifier mismatch
374
- await expect(
375
- runtime.processToolCall(
376
- "protectedTool",
377
- { arg: "value" },
378
- mockHandler,
379
- sessionWithUserA
380
- )
381
- ).rejects.toThrow(DelegationRequiredError);
382
-
383
- expect(mockAccessControlService.verifyDelegation).toHaveBeenCalled();
384
- expect(mockHandler).not.toHaveBeenCalled();
385
- });
386
-
387
- it("should accept delegation when user_identifier matches session userDid", async () => {
388
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
389
- requiresDelegation: true,
390
- requiredScopes: ["files:write"],
391
- });
392
-
393
- // Mock verification response with User A's user_identifier
394
- mockAccessControlService.verifyDelegation.mockResolvedValue({
395
- success: true,
396
- data: {
397
- valid: true,
398
- delegation_id: "test-delegation-id",
399
- credential: {
400
- agent_did: "did:key:zmock123",
401
- user_identifier: userDidA, // ✅ User A's identifier matches session
402
- scopes: ["files:write"],
403
- issued_at: Date.now(),
404
- created_at: Date.now(),
405
- },
406
- },
407
- } as VerifyDelegationAPIResponse);
408
-
409
- // Session with User A's DID
410
- const sessionWithUserA = {
411
- ...session,
412
- userDid: userDidA, // User A's DID
413
- delegationToken: "delegation-token-for-user-a",
414
- };
415
-
416
- // Should accept delegation when user_identifier matches
417
- const result = await runtime.processToolCall(
418
- "protectedTool",
419
- { arg: "value" },
420
- mockHandler,
421
- sessionWithUserA
422
- );
423
-
424
- expect(mockAccessControlService.verifyDelegation).toHaveBeenCalled();
425
- expect(mockHandler).toHaveBeenCalledWith({ arg: "value" });
426
- expect(result).toEqual({ result: "success" });
427
- });
428
-
429
- it("should handle missing user_identifier gracefully (backward compatibility)", async () => {
430
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
431
- requiresDelegation: true,
432
- requiredScopes: ["files:write"],
433
- });
434
-
435
- // Mock verification response without user_identifier (legacy format)
436
- mockAccessControlService.verifyDelegation.mockResolvedValue({
437
- success: true,
438
- data: {
439
- valid: true,
440
- delegation_id: "test-delegation-id",
441
- credential: {
442
- agent_did: "did:key:zmock123",
443
- // No user_identifier field
444
- scopes: ["files:write"],
445
- issued_at: Date.now(),
446
- created_at: Date.now(),
447
- },
448
- },
449
- } as VerifyDelegationAPIResponse);
450
-
451
- const sessionWithUserA = {
452
- ...session,
453
- userDid: userDidA,
454
- delegationToken: "legacy-delegation-token",
455
- };
456
-
457
- // Should allow execution when user_identifier is missing (backward compatibility)
458
- const result = await runtime.processToolCall(
459
- "protectedTool",
460
- { arg: "value" },
461
- mockHandler,
462
- sessionWithUserA
463
- );
464
-
465
- expect(mockHandler).toHaveBeenCalledWith({ arg: "value" });
466
- expect(result).toEqual({ result: "success" });
467
- });
468
-
469
- it("should handle missing session userDid gracefully", async () => {
470
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
471
- requiresDelegation: true,
472
- requiredScopes: ["files:write"],
473
- });
474
-
475
- // Mock verification response with user_identifier
476
- mockAccessControlService.verifyDelegation.mockResolvedValue({
477
- success: true,
478
- data: {
479
- valid: true,
480
- delegation_id: "test-delegation-id",
481
- credential: {
482
- agent_did: "did:key:zmock123",
483
- user_identifier: userDidA,
484
- scopes: ["files:write"],
485
- issued_at: Date.now(),
486
- created_at: Date.now(),
487
- },
488
- },
489
- } as VerifyDelegationAPIResponse);
490
-
491
- // Session without userDid (legacy format)
492
- const sessionWithoutUserDid = {
493
- ...session,
494
- // No userDid field
495
- delegationToken: "delegation-token",
496
- };
497
-
498
- // Should allow execution when session userDid is missing (backward compatibility)
499
- const result = await runtime.processToolCall(
500
- "protectedTool",
501
- { arg: "value" },
502
- mockHandler,
503
- sessionWithoutUserDid
504
- );
505
-
506
- expect(mockHandler).toHaveBeenCalledWith({ arg: "value" });
507
- expect(result).toEqual({ result: "success" });
508
- });
509
- });
510
-
511
- it("should create proof after successful tool execution", async () => {
512
- mockToolProtectionService.checkToolProtection.mockResolvedValue(null);
513
-
514
- await runtime.processToolCall(
515
- "unprotectedTool",
516
- { arg: "value" },
517
- mockHandler,
518
- session
519
- );
520
-
521
- const proof = runtime.getLastProof();
522
- expect(proof).toBeDefined();
523
- expect(proof.did).toBe("did:key:zmock123");
524
- expect(proof.sessionId).toBe("session123");
525
- });
526
-
527
- it("should not create proof when tool execution is blocked", async () => {
528
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
529
- requiresDelegation: true,
530
- requiredScopes: ["files:write"],
531
- });
532
-
533
- const initialProof = runtime.getLastProof();
534
-
535
- await expect(
536
- runtime.processToolCall(
537
- "protectedTool",
538
- { arg: "value" },
539
- mockHandler,
540
- session
541
- )
542
- ).rejects.toThrow(DelegationRequiredError);
543
-
544
- // Proof should not be created when tool is blocked
545
- const proofAfterBlock = runtime.getLastProof();
546
- expect(proofAfterBlock).toBe(initialProof);
547
- });
548
- });
549
-
550
- describe("DelegationRequiredError details", () => {
551
- const mockHandler = vi.fn().mockResolvedValue({ result: "success" });
552
- const session = {
553
- id: "session123",
554
- audience: "https://client.example.com",
555
- nonce: "test-nonce",
556
- };
557
-
558
- it("should include tool name in error", async () => {
559
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
560
- requiresDelegation: true,
561
- requiredScopes: ["files:write"],
562
- });
563
-
564
- try {
565
- await runtime.processToolCall(
566
- "protectedTool",
567
- { arg: "value" },
568
- mockHandler,
569
- session
570
- );
571
- expect.fail("Should have thrown DelegationRequiredError");
572
- } catch (error: any) {
573
- expect(error).toBeInstanceOf(DelegationRequiredError);
574
- expect(error.toolName).toBe("protectedTool");
575
- expect(error.message).toContain("protectedTool");
576
- }
577
- });
578
-
579
- it("should include required scopes in error", async () => {
580
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
581
- requiresDelegation: true,
582
- requiredScopes: ["files:write", "files:read"],
583
- });
584
-
585
- try {
586
- await runtime.processToolCall(
587
- "protectedTool",
588
- { arg: "value" },
589
- mockHandler,
590
- session
591
- );
592
- expect.fail("Should have thrown DelegationRequiredError");
593
- } catch (error: any) {
594
- expect(error).toBeInstanceOf(DelegationRequiredError);
595
- expect(error.requiredScopes).toEqual(["files:write", "files:read"]);
596
- }
597
- });
598
-
599
- it("should include consent URL in error", async () => {
600
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
601
- requiresDelegation: true,
602
- requiredScopes: ["files:write"],
603
- });
604
-
605
- try {
606
- await runtime.processToolCall(
607
- "protectedTool",
608
- { arg: "value" },
609
- mockHandler,
610
- session
611
- );
612
- expect.fail("Should have thrown DelegationRequiredError");
613
- } catch (error: any) {
614
- expect(error).toBeInstanceOf(DelegationRequiredError);
615
- expect(error.consentUrl).toBeDefined();
616
- expect(error.consentUrl).toContain("kya.vouched.id/bouncer/consent");
617
- expect(error.consentUrl).toContain("tool=protectedTool");
618
- expect(error.consentUrl).toContain("scopes=files%3Awrite");
619
- }
620
- });
621
-
622
- it("should include resume token in error", async () => {
623
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
624
- requiresDelegation: true,
625
- requiredScopes: ["files:write"],
626
- });
627
-
628
- try {
629
- await runtime.processToolCall(
630
- "protectedTool",
631
- { arg: "value" },
632
- mockHandler,
633
- session
634
- );
635
- expect.fail("Should have thrown DelegationRequiredError");
636
- } catch (error: any) {
637
- expect(error).toBeInstanceOf(DelegationRequiredError);
638
- expect(error.resumeToken).toBeDefined();
639
- expect(error.resumeToken).toMatch(/^resume_/);
640
- }
641
- });
642
-
643
- it("should include intercepted call context in error", async () => {
644
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
645
- requiresDelegation: true,
646
- requiredScopes: ["files:write"],
647
- });
648
-
649
- try {
650
- await runtime.processToolCall(
651
- "protectedTool",
652
- { arg: "value", nested: { data: "test" } },
653
- mockHandler,
654
- session
655
- );
656
- expect.fail("Should have thrown DelegationRequiredError");
657
- } catch (error: any) {
658
- expect(error).toBeInstanceOf(DelegationRequiredError);
659
- expect(error.interceptedCall).toBeDefined();
660
- expect(error.interceptedCall.toolName).toBe("protectedTool");
661
- expect(error.interceptedCall.args).toEqual({
662
- arg: "value",
663
- nested: { data: "test" },
664
- });
665
- expect(error.interceptedCall.sessionId).toBe("session123");
666
- }
667
- });
668
- });
669
-
670
- describe("audit logging", () => {
671
- const mockHandler = vi.fn().mockResolvedValue({ result: "success" });
672
- const session = {
673
- id: "session123",
674
- audience: "https://client.example.com",
675
- nonce: "test-nonce",
676
- };
677
-
678
- it("should log tool protection check when audit enabled", async () => {
679
- mockToolProtectionService.checkToolProtection.mockResolvedValue(null);
680
-
681
- await runtime.processToolCall(
682
- "testTool",
683
- { arg: "value" },
684
- mockHandler,
685
- session
686
- );
687
-
688
- expect(config.audit!.logFunction).toHaveBeenCalled();
689
- const logCalls = (config.audit!.logFunction as any).mock.calls;
690
- const protectionLogCall = logCalls.find((call: any[]) => {
691
- try {
692
- const log = JSON.parse(call[0]);
693
- return log.event === "tool_executed";
694
- } catch {
695
- return false;
696
- }
697
- });
698
- expect(protectionLogCall).toBeDefined();
699
- });
700
-
701
- it("should log blocked tool call when audit enabled", async () => {
702
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
703
- requiresDelegation: true,
704
- requiredScopes: ["files:write"],
705
- });
706
-
707
- const consoleWarnSpy = vi
708
- .spyOn(console, "warn")
709
- .mockImplementation(() => {});
710
-
711
- try {
712
- await runtime.processToolCall(
713
- "protectedTool",
714
- { arg: "value" },
715
- mockHandler,
716
- session
717
- );
718
- } catch (error) {
719
- // Expected
720
- }
721
-
722
- expect(consoleWarnSpy).toHaveBeenCalledWith(
723
- expect.stringContaining("[MCP-I] BLOCKED"),
724
- expect.any(Object)
725
- );
726
-
727
- consoleWarnSpy.mockRestore();
728
- });
729
-
730
- it("should not log when audit disabled", async () => {
731
- const auditLogFn = vi.fn();
732
- const configNoAudit = {
733
- ...config,
734
- audit: {
735
- enabled: false,
736
- logFunction: auditLogFn,
737
- },
738
- };
739
- const runtimeNoAudit = new MCPIRuntimeBase(configNoAudit);
740
- await runtimeNoAudit.initialize();
741
-
742
- mockToolProtectionService.checkToolProtection.mockResolvedValue(null);
743
-
744
- await runtimeNoAudit.processToolCall(
745
- "testTool",
746
- { arg: "value" },
747
- mockHandler,
748
- session
749
- );
750
-
751
- // Should not call log function for tool execution (initialization may still log)
752
- const toolExecutionLogs = auditLogFn.mock.calls.filter((call: any[]) => {
753
- try {
754
- const log = JSON.parse(call[0]);
755
- return log.event === "tool_executed";
756
- } catch {
757
- return false;
758
- }
759
- });
760
- expect(toolExecutionLogs.length).toBe(0);
761
- });
762
- });
763
-
764
- describe("tool protection service integration", () => {
765
- const mockHandler = vi.fn().mockResolvedValue({ result: "success" });
766
- const session = {
767
- id: "session123",
768
- audience: "https://client.example.com",
769
- nonce: "test-nonce",
770
- };
771
-
772
- it("should use agent DID from identity for protection check", async () => {
773
- mockToolProtectionService.checkToolProtection.mockResolvedValue(null);
774
-
775
- await runtime.processToolCall(
776
- "testTool",
777
- { arg: "value" },
778
- mockHandler,
779
- session
780
- );
781
-
782
- expect(
783
- mockToolProtectionService.checkToolProtection
784
- ).toHaveBeenCalledWith("testTool", "did:key:zmock123");
785
- });
786
-
787
- it("should handle tool protection service errors gracefully", async () => {
788
- mockToolProtectionService.checkToolProtection.mockRejectedValue(
789
- new Error("Service unavailable")
790
- );
791
-
792
- // If the service throws, the error propagates and tool execution is blocked
793
- // This is the current behavior - service errors prevent tool execution
794
- await expect(
795
- runtime.processToolCall(
796
- "testTool",
797
- { arg: "value" },
798
- mockHandler,
799
- session
800
- )
801
- ).rejects.toThrow("Service unavailable");
802
- });
803
-
804
- it("should work without tool protection service", async () => {
805
- const configNoService = {
806
- ...config,
807
- toolProtectionService: undefined,
808
- };
809
- const runtimeNoService = new MCPIRuntimeBase(configNoService);
810
- await runtimeNoService.initialize();
811
-
812
- const result = await runtimeNoService.processToolCall(
813
- "testTool",
814
- { arg: "value" },
815
- mockHandler,
816
- session
817
- );
818
-
819
- expect(mockHandler).toHaveBeenCalled();
820
- expect(result).toEqual({ result: "success" });
821
- });
822
- });
823
-
824
- describe("edge cases", () => {
825
- const mockHandler = vi.fn().mockResolvedValue({ result: "success" });
826
- const session = {
827
- id: "session123",
828
- audience: "https://client.example.com",
829
- nonce: "test-nonce",
830
- };
831
-
832
- it("should handle empty required scopes array", async () => {
833
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
834
- requiresDelegation: true,
835
- requiredScopes: [],
836
- });
837
-
838
- await expect(
839
- runtime.processToolCall(
840
- "protectedTool",
841
- { arg: "value" },
842
- mockHandler,
843
- session
844
- )
845
- ).rejects.toThrow(DelegationRequiredError);
846
- });
847
-
848
- it("should handle multiple required scopes", async () => {
849
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
850
- requiresDelegation: true,
851
- requiredScopes: ["scope1", "scope2", "scope3"],
852
- });
853
-
854
- try {
855
- await runtime.processToolCall(
856
- "protectedTool",
857
- { arg: "value" },
858
- mockHandler,
859
- session
860
- );
861
- expect.fail("Should have thrown DelegationRequiredError");
862
- } catch (error: any) {
863
- expect(error.requiredScopes).toEqual(["scope1", "scope2", "scope3"]);
864
- expect(error.consentUrl).toContain("scopes=scope1%2Cscope2%2Cscope3");
865
- }
866
- });
867
-
868
- it("should handle session without id", async () => {
869
- mockToolProtectionService.checkToolProtection.mockResolvedValue({
870
- requiresDelegation: true,
871
- requiredScopes: ["files:write"],
872
- });
873
-
874
- const sessionWithoutId = {
875
- audience: "https://client.example.com",
876
- nonce: "test-nonce",
877
- };
878
-
879
- try {
880
- await runtime.processToolCall(
881
- "protectedTool",
882
- { arg: "value" },
883
- mockHandler,
884
- sessionWithoutId
885
- );
886
- expect.fail("Should have thrown DelegationRequiredError");
887
- } catch (error: any) {
888
- expect(error.interceptedCall.sessionId).toBe("unknown");
889
- }
890
- });
891
-
892
- it("should handle handler errors independently of protection", async () => {
893
- mockToolProtectionService.checkToolProtection.mockResolvedValue(null);
894
- const errorHandler = vi
895
- .fn()
896
- .mockRejectedValue(new Error("Handler failed"));
897
-
898
- await expect(
899
- runtime.processToolCall(
900
- "errorTool",
901
- { arg: "value" },
902
- errorHandler,
903
- session
904
- )
905
- ).rejects.toThrow("Handler failed");
906
- });
907
- });
908
- });