@kya-os/mcp-i-core 1.3.12 → 1.3.14
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/dist/config/remote-config.js +9 -12
- package/dist/runtime/base.js +11 -0
- package/dist/services/access-control.service.js +5 -0
- package/dist/services/tool-protection.service.js +17 -8
- package/package.json +2 -2
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-test$colon$coverage.log +0 -4586
- package/.turbo/turbo-test.log +0 -3169
- package/COMPLIANCE_IMPROVEMENT_REPORT.md +0 -483
- package/Composer 3.md +0 -615
- package/GPT-5.md +0 -1169
- package/OPUS-plan.md +0 -352
- package/PHASE_3_AND_4.1_SUMMARY.md +0 -585
- package/PHASE_3_SUMMARY.md +0 -317
- package/PHASE_4.1.3_SUMMARY.md +0 -428
- package/PHASE_4.1_COMPLETE.md +0 -525
- package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +0 -1240
- package/SCHEMA_COMPLIANCE_REPORT.md +0 -275
- package/TEST_PLAN.md +0 -571
- package/coverage/coverage-final.json +0 -60
- package/dist/cache/oauth-config-cache.d.ts.map +0 -1
- package/dist/cache/oauth-config-cache.js.map +0 -1
- package/dist/cache/tool-protection-cache.d.ts.map +0 -1
- package/dist/cache/tool-protection-cache.js.map +0 -1
- package/dist/compliance/index.d.ts.map +0 -1
- package/dist/compliance/index.js.map +0 -1
- package/dist/compliance/schema-registry.d.ts.map +0 -1
- package/dist/compliance/schema-registry.js.map +0 -1
- package/dist/compliance/schema-verifier.d.ts.map +0 -1
- package/dist/compliance/schema-verifier.js.map +0 -1
- package/dist/config/remote-config.d.ts.map +0 -1
- package/dist/config/remote-config.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/delegation/audience-validator.d.ts.map +0 -1
- package/dist/delegation/audience-validator.js.map +0 -1
- package/dist/delegation/bitstring.d.ts.map +0 -1
- package/dist/delegation/bitstring.js.map +0 -1
- package/dist/delegation/cascading-revocation.d.ts.map +0 -1
- package/dist/delegation/cascading-revocation.js.map +0 -1
- package/dist/delegation/delegation-graph.d.ts.map +0 -1
- package/dist/delegation/delegation-graph.js.map +0 -1
- package/dist/delegation/did-key-resolver.d.ts.map +0 -1
- package/dist/delegation/did-key-resolver.js.map +0 -1
- package/dist/delegation/index.d.ts.map +0 -1
- package/dist/delegation/index.js.map +0 -1
- package/dist/delegation/statuslist-manager.d.ts.map +0 -1
- package/dist/delegation/statuslist-manager.js.map +0 -1
- package/dist/delegation/storage/index.d.ts.map +0 -1
- package/dist/delegation/storage/index.js.map +0 -1
- package/dist/delegation/storage/memory-graph-storage.d.ts.map +0 -1
- package/dist/delegation/storage/memory-graph-storage.js.map +0 -1
- package/dist/delegation/storage/memory-statuslist-storage.d.ts.map +0 -1
- package/dist/delegation/storage/memory-statuslist-storage.js.map +0 -1
- package/dist/delegation/utils.d.ts.map +0 -1
- package/dist/delegation/utils.js.map +0 -1
- package/dist/delegation/vc-issuer.d.ts.map +0 -1
- package/dist/delegation/vc-issuer.js.map +0 -1
- package/dist/delegation/vc-verifier.d.ts.map +0 -1
- package/dist/delegation/vc-verifier.js.map +0 -1
- package/dist/identity/idp-token-resolver.d.ts.map +0 -1
- package/dist/identity/idp-token-resolver.js.map +0 -1
- package/dist/identity/idp-token-storage.interface.d.ts.map +0 -1
- package/dist/identity/idp-token-storage.interface.js.map +0 -1
- package/dist/identity/user-did-manager.d.ts.map +0 -1
- package/dist/identity/user-did-manager.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers/base.d.ts.map +0 -1
- package/dist/providers/base.js.map +0 -1
- package/dist/providers/memory.d.ts.map +0 -1
- package/dist/providers/memory.js.map +0 -1
- package/dist/runtime/audit-logger.d.ts.map +0 -1
- package/dist/runtime/audit-logger.js.map +0 -1
- package/dist/runtime/base.d.ts.map +0 -1
- package/dist/runtime/base.js.map +0 -1
- package/dist/services/access-control.service.d.ts.map +0 -1
- package/dist/services/access-control.service.js.map +0 -1
- package/dist/services/authorization/authorization-registry.d.ts.map +0 -1
- package/dist/services/authorization/authorization-registry.js.map +0 -1
- package/dist/services/authorization/types.d.ts.map +0 -1
- package/dist/services/authorization/types.js.map +0 -1
- package/dist/services/batch-delegation.service.d.ts.map +0 -1
- package/dist/services/batch-delegation.service.js.map +0 -1
- package/dist/services/crypto.service.d.ts.map +0 -1
- package/dist/services/crypto.service.js.map +0 -1
- package/dist/services/errors.d.ts.map +0 -1
- package/dist/services/errors.js.map +0 -1
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/index.js.map +0 -1
- package/dist/services/oauth-config.service.d.ts.map +0 -1
- package/dist/services/oauth-config.service.js.map +0 -1
- package/dist/services/oauth-provider-registry.d.ts.map +0 -1
- package/dist/services/oauth-provider-registry.js.map +0 -1
- package/dist/services/oauth-service.d.ts.map +0 -1
- package/dist/services/oauth-service.js.map +0 -1
- package/dist/services/oauth-token-retrieval.service.d.ts.map +0 -1
- package/dist/services/oauth-token-retrieval.service.js.map +0 -1
- package/dist/services/proof-verifier.d.ts.map +0 -1
- package/dist/services/proof-verifier.js.map +0 -1
- package/dist/services/provider-resolver.d.ts.map +0 -1
- package/dist/services/provider-resolver.js.map +0 -1
- package/dist/services/provider-validator.d.ts.map +0 -1
- package/dist/services/provider-validator.js.map +0 -1
- package/dist/services/session-registration.service.d.ts.map +0 -1
- package/dist/services/session-registration.service.js.map +0 -1
- package/dist/services/storage.service.d.ts.map +0 -1
- package/dist/services/storage.service.js.map +0 -1
- package/dist/services/tool-context-builder.d.ts.map +0 -1
- package/dist/services/tool-context-builder.js.map +0 -1
- package/dist/services/tool-protection.service.d.ts.map +0 -1
- package/dist/services/tool-protection.service.js.map +0 -1
- package/dist/types/oauth-required-error.d.ts.map +0 -1
- package/dist/types/oauth-required-error.js.map +0 -1
- package/dist/types/tool-protection.d.ts.map +0 -1
- package/dist/types/tool-protection.js.map +0 -1
- package/dist/utils/base58.d.ts.map +0 -1
- package/dist/utils/base58.js.map +0 -1
- package/dist/utils/base64.d.ts.map +0 -1
- package/dist/utils/base64.js.map +0 -1
- package/dist/utils/cors.d.ts.map +0 -1
- package/dist/utils/cors.js.map +0 -1
- package/dist/utils/did-helpers.d.ts.map +0 -1
- package/dist/utils/did-helpers.js.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/storage-keys.d.ts.map +0 -1
- package/dist/utils/storage-keys.js.map +0 -1
- package/docs/API_REFERENCE.md +0 -1362
- package/docs/COMPLIANCE_MATRIX.md +0 -691
- package/docs/STATUSLIST2021_GUIDE.md +0 -696
- package/docs/W3C_VC_DELEGATION_GUIDE.md +0 -710
- package/src/__tests__/cache/tool-protection-cache.test.ts +0 -640
- package/src/__tests__/config/provider-runtime-config.test.ts +0 -309
- package/src/__tests__/delegation-e2e.test.ts +0 -690
- package/src/__tests__/identity/user-did-manager.test.ts +0 -232
- package/src/__tests__/index.test.ts +0 -56
- package/src/__tests__/integration/full-flow.test.ts +0 -789
- package/src/__tests__/integration.test.ts +0 -281
- package/src/__tests__/providers/base.test.ts +0 -173
- package/src/__tests__/providers/memory.test.ts +0 -319
- package/src/__tests__/regression/phase2-regression.test.ts +0 -429
- package/src/__tests__/runtime/audit-logger.test.ts +0 -154
- package/src/__tests__/runtime/base-extensions.test.ts +0 -595
- package/src/__tests__/runtime/base.test.ts +0 -869
- package/src/__tests__/runtime/delegation-flow.test.ts +0 -164
- package/src/__tests__/runtime/proof-client-did.test.ts +0 -376
- package/src/__tests__/runtime/route-interception.test.ts +0 -686
- package/src/__tests__/runtime/tool-protection-enforcement.test.ts +0 -908
- package/src/__tests__/services/agentshield-integration.test.ts +0 -791
- package/src/__tests__/services/cache-busting.test.ts +0 -125
- package/src/__tests__/services/oauth-service-pkce.test.ts +0 -556
- package/src/__tests__/services/provider-resolver-edge-cases.test.ts +0 -591
- package/src/__tests__/services/tool-protection-merged-config.test.ts +0 -485
- package/src/__tests__/services/tool-protection-oauth-provider.test.ts +0 -480
- package/src/__tests__/services/tool-protection.service.test.ts +0 -1373
- package/src/__tests__/utils/mock-providers.ts +0 -340
- package/src/cache/oauth-config-cache.d.ts +0 -69
- package/src/cache/oauth-config-cache.d.ts.map +0 -1
- package/src/cache/oauth-config-cache.js.map +0 -1
- package/src/cache/oauth-config-cache.ts +0 -123
- package/src/cache/tool-protection-cache.ts +0 -171
- package/src/compliance/EXAMPLE.md +0 -412
- package/src/compliance/__tests__/schema-verifier.test.ts +0 -797
- package/src/compliance/index.ts +0 -8
- package/src/compliance/schema-registry.ts +0 -460
- package/src/compliance/schema-verifier.ts +0 -708
- package/src/config/__tests__/merged-config.spec.ts +0 -445
- package/src/config/__tests__/remote-config.spec.ts +0 -268
- package/src/config/remote-config.ts +0 -264
- package/src/config.ts +0 -312
- package/src/delegation/__tests__/audience-validator.test.ts +0 -112
- package/src/delegation/__tests__/bitstring.test.ts +0 -346
- package/src/delegation/__tests__/cascading-revocation.test.ts +0 -628
- package/src/delegation/__tests__/delegation-graph.test.ts +0 -584
- package/src/delegation/__tests__/did-key-resolver.test.ts +0 -265
- package/src/delegation/__tests__/utils.test.ts +0 -152
- package/src/delegation/__tests__/vc-issuer.test.ts +0 -442
- package/src/delegation/__tests__/vc-verifier.test.ts +0 -922
- package/src/delegation/audience-validator.ts +0 -52
- package/src/delegation/bitstring.ts +0 -278
- package/src/delegation/cascading-revocation.ts +0 -370
- package/src/delegation/delegation-graph.ts +0 -299
- package/src/delegation/did-key-resolver.ts +0 -179
- package/src/delegation/index.ts +0 -14
- package/src/delegation/statuslist-manager.ts +0 -353
- package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +0 -366
- package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +0 -228
- package/src/delegation/storage/index.ts +0 -9
- package/src/delegation/storage/memory-graph-storage.ts +0 -178
- package/src/delegation/storage/memory-statuslist-storage.ts +0 -77
- package/src/delegation/utils.ts +0 -221
- package/src/delegation/vc-issuer.ts +0 -232
- package/src/delegation/vc-verifier.ts +0 -568
- package/src/identity/idp-token-resolver.ts +0 -181
- package/src/identity/idp-token-storage.interface.ts +0 -94
- package/src/identity/user-did-manager.ts +0 -526
- package/src/index.ts +0 -310
- package/src/providers/base.d.ts +0 -91
- package/src/providers/base.d.ts.map +0 -1
- package/src/providers/base.js.map +0 -1
- package/src/providers/base.ts +0 -96
- package/src/providers/memory.ts +0 -142
- package/src/runtime/audit-logger.ts +0 -39
- package/src/runtime/base.ts +0 -1392
- package/src/services/__tests__/access-control.integration.test.ts +0 -443
- package/src/services/__tests__/access-control.proof-response-validation.test.ts +0 -578
- package/src/services/__tests__/access-control.service.test.ts +0 -970
- package/src/services/__tests__/batch-delegation.service.test.ts +0 -351
- package/src/services/__tests__/crypto.service.test.ts +0 -531
- package/src/services/__tests__/oauth-provider-registry.test.ts +0 -142
- package/src/services/__tests__/proof-verifier.integration.test.ts +0 -485
- package/src/services/__tests__/proof-verifier.test.ts +0 -489
- package/src/services/__tests__/provider-resolution.integration.test.ts +0 -202
- package/src/services/__tests__/provider-resolver.test.ts +0 -213
- package/src/services/__tests__/storage.service.test.ts +0 -358
- package/src/services/access-control.service.ts +0 -990
- package/src/services/authorization/authorization-registry.ts +0 -66
- package/src/services/authorization/types.ts +0 -71
- package/src/services/batch-delegation.service.ts +0 -137
- package/src/services/crypto.service.ts +0 -302
- package/src/services/errors.ts +0 -76
- package/src/services/index.ts +0 -18
- package/src/services/oauth-config.service.d.ts +0 -53
- package/src/services/oauth-config.service.d.ts.map +0 -1
- package/src/services/oauth-config.service.js.map +0 -1
- package/src/services/oauth-config.service.ts +0 -192
- package/src/services/oauth-provider-registry.d.ts +0 -57
- package/src/services/oauth-provider-registry.d.ts.map +0 -1
- package/src/services/oauth-provider-registry.js.map +0 -1
- package/src/services/oauth-provider-registry.ts +0 -141
- package/src/services/oauth-service.ts +0 -544
- package/src/services/oauth-token-retrieval.service.ts +0 -245
- package/src/services/proof-verifier.ts +0 -478
- package/src/services/provider-resolver.d.ts +0 -48
- package/src/services/provider-resolver.d.ts.map +0 -1
- package/src/services/provider-resolver.js.map +0 -1
- package/src/services/provider-resolver.ts +0 -146
- package/src/services/provider-validator.ts +0 -170
- package/src/services/session-registration.service.ts +0 -251
- package/src/services/storage.service.ts +0 -566
- package/src/services/tool-context-builder.ts +0 -237
- package/src/services/tool-protection.service.ts +0 -1070
- package/src/types/oauth-required-error.ts +0 -63
- package/src/types/tool-protection.ts +0 -155
- package/src/utils/__tests__/did-helpers.test.ts +0 -156
- package/src/utils/base58.ts +0 -109
- package/src/utils/base64.ts +0 -148
- package/src/utils/cors.ts +0 -83
- package/src/utils/did-helpers.ts +0 -210
- package/src/utils/index.ts +0 -8
- package/src/utils/storage-keys.ts +0 -278
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -56
|
@@ -1,789 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Full Flow Integration Tests
|
|
3
|
-
*
|
|
4
|
-
* End-to-end integration tests covering:
|
|
5
|
-
* - Complete handshake → tool call → proof flow
|
|
6
|
-
* - Tool protection enforcement
|
|
7
|
-
* - Delegation flow (intercept → authorize → resume)
|
|
8
|
-
* - AgentShield integration
|
|
9
|
-
* - Caching behavior
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, test, expect, beforeEach, vi, afterEach } from "vitest";
|
|
13
|
-
import { MCPIRuntimeBase } from "../../runtime/base";
|
|
14
|
-
import { ToolProtectionService } from "../../services/tool-protection.service";
|
|
15
|
-
import {
|
|
16
|
-
InMemoryToolProtectionCache,
|
|
17
|
-
type ToolProtectionCache,
|
|
18
|
-
} from "../../cache/tool-protection-cache";
|
|
19
|
-
import {
|
|
20
|
-
MemoryStorageProvider,
|
|
21
|
-
MemoryNonceCacheProvider,
|
|
22
|
-
MemoryIdentityProvider,
|
|
23
|
-
} from "../../providers/memory";
|
|
24
|
-
import {
|
|
25
|
-
createMockProviders,
|
|
26
|
-
MockCryptoProvider,
|
|
27
|
-
MockClockProvider,
|
|
28
|
-
MockFetchProvider,
|
|
29
|
-
} from "../utils/mock-providers";
|
|
30
|
-
import type { ToolProtectionServiceConfig } from "../../types/tool-protection";
|
|
31
|
-
import type { ProviderRuntimeConfig } from "../../config";
|
|
32
|
-
|
|
33
|
-
// Type declaration for global (polyfilled by Vitest)
|
|
34
|
-
declare const global: typeof globalThis;
|
|
35
|
-
|
|
36
|
-
// Mock global fetch
|
|
37
|
-
global.fetch = vi.fn();
|
|
38
|
-
|
|
39
|
-
describe("Full Flow Integration", () => {
|
|
40
|
-
let runtime: MCPIRuntimeBase;
|
|
41
|
-
let toolProtectionService: ToolProtectionService;
|
|
42
|
-
let cache: ToolProtectionCache;
|
|
43
|
-
let config: ProviderRuntimeConfig;
|
|
44
|
-
let mockProviders: ReturnType<typeof createMockProviders>;
|
|
45
|
-
const mockAgentDid =
|
|
46
|
-
"did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
|
|
47
|
-
|
|
48
|
-
beforeEach(async () => {
|
|
49
|
-
vi.clearAllMocks();
|
|
50
|
-
mockProviders = createMockProviders();
|
|
51
|
-
|
|
52
|
-
cache = new InMemoryToolProtectionCache();
|
|
53
|
-
const toolProtectionConfig: ToolProtectionServiceConfig = {
|
|
54
|
-
apiUrl: "https://kya.vouched.id",
|
|
55
|
-
apiKey: "test-api-key",
|
|
56
|
-
projectId: "test-project",
|
|
57
|
-
cacheTtl: 300000,
|
|
58
|
-
debug: false,
|
|
59
|
-
};
|
|
60
|
-
toolProtectionService = new ToolProtectionService(
|
|
61
|
-
toolProtectionConfig,
|
|
62
|
-
cache
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
config = {
|
|
66
|
-
...mockProviders,
|
|
67
|
-
environment: "development",
|
|
68
|
-
session: {
|
|
69
|
-
timestampSkewSeconds: 120,
|
|
70
|
-
ttlMinutes: 30,
|
|
71
|
-
},
|
|
72
|
-
audit: {
|
|
73
|
-
enabled: true,
|
|
74
|
-
logFunction: vi.fn(),
|
|
75
|
-
includePayloads: true,
|
|
76
|
-
},
|
|
77
|
-
toolProtection: {
|
|
78
|
-
source: "agentshield",
|
|
79
|
-
agentShield: {
|
|
80
|
-
apiUrl: "https://kya.vouched.id",
|
|
81
|
-
apiKey: "test-api-key",
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
runtime = new MCPIRuntimeBase(config);
|
|
87
|
-
await runtime.initialize();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
afterEach(() => {
|
|
91
|
-
vi.restoreAllMocks();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe("Complete handshake → tool call → proof flow", () => {
|
|
95
|
-
test("should complete full authentication and tool execution cycle", async () => {
|
|
96
|
-
// Step 1: Client initiates handshake
|
|
97
|
-
const handshakeRequest = {
|
|
98
|
-
clientDid: "did:key:zclient789",
|
|
99
|
-
audience: "https://app.example.com",
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const handshakeResponse = await runtime.handleHandshake(handshakeRequest);
|
|
103
|
-
|
|
104
|
-
expect(handshakeResponse.sessionId).toBeDefined();
|
|
105
|
-
expect(handshakeResponse.agentDid).toMatch(/^did:key:z/);
|
|
106
|
-
expect(handshakeResponse.signature).toBeDefined();
|
|
107
|
-
expect(handshakeResponse.capabilities).toContain("identity");
|
|
108
|
-
|
|
109
|
-
// Step 2: Get session and issue nonce
|
|
110
|
-
const session = await runtime.getCurrentSession();
|
|
111
|
-
expect(session).toBeDefined();
|
|
112
|
-
expect(session?.clientDid).toBe(handshakeRequest.clientDid);
|
|
113
|
-
expect(session?.audience).toBe(handshakeRequest.audience);
|
|
114
|
-
|
|
115
|
-
const nonce = await runtime.issueNonce(session!.id);
|
|
116
|
-
session!.nonce = nonce;
|
|
117
|
-
|
|
118
|
-
// Step 3: Execute unprotected tool
|
|
119
|
-
const toolHandler = async (args: any) => {
|
|
120
|
-
return {
|
|
121
|
-
message: `Hello ${args.name}!`,
|
|
122
|
-
timestamp: Date.now(),
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const result = await runtime.processToolCall(
|
|
127
|
-
"greetingTool",
|
|
128
|
-
{ name: "Alice" },
|
|
129
|
-
toolHandler,
|
|
130
|
-
session
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
expect(result.message).toBe("Hello Alice!");
|
|
134
|
-
|
|
135
|
-
// Step 4: Verify proof was created
|
|
136
|
-
const proof = runtime.getLastProof();
|
|
137
|
-
expect(proof).toBeDefined();
|
|
138
|
-
expect(proof?.nonce).toBe(nonce);
|
|
139
|
-
expect(proof?.sessionId).toBe(session!.id);
|
|
140
|
-
expect(proof?.audience).toBe("https://app.example.com");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("should handle multiple tool calls in sequence", async () => {
|
|
144
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
145
|
-
clientDid: "did:key:zclient789",
|
|
146
|
-
audience: "https://app.example.com",
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
const session = await runtime.getCurrentSession();
|
|
150
|
-
expect(session).toBeDefined();
|
|
151
|
-
|
|
152
|
-
const nonce1 = await runtime.issueNonce(session!.id);
|
|
153
|
-
session!.nonce = nonce1;
|
|
154
|
-
|
|
155
|
-
// First tool call
|
|
156
|
-
const result1 = await runtime.processToolCall(
|
|
157
|
-
"tool1",
|
|
158
|
-
{ arg: "value1" },
|
|
159
|
-
async (args) => ({ result: args.arg }),
|
|
160
|
-
session
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
expect(result1.result).toBe("value1");
|
|
164
|
-
|
|
165
|
-
// Second tool call with new nonce
|
|
166
|
-
const nonce2 = await runtime.issueNonce(session!.id);
|
|
167
|
-
session!.nonce = nonce2;
|
|
168
|
-
|
|
169
|
-
const result2 = await runtime.processToolCall(
|
|
170
|
-
"tool2",
|
|
171
|
-
{ arg: "value2" },
|
|
172
|
-
async (args) => ({ result: args.arg }),
|
|
173
|
-
session
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
expect(result2.result).toBe("value2");
|
|
177
|
-
|
|
178
|
-
// Verify both proofs were created
|
|
179
|
-
const proof = runtime.getLastProof();
|
|
180
|
-
expect(proof?.nonce).toBe(nonce2);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test("should propagate client DID through entire flow", async () => {
|
|
184
|
-
const clientDid = "did:key:zclient789";
|
|
185
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
186
|
-
clientDid,
|
|
187
|
-
audience: "https://app.example.com",
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const session = await runtime.getCurrentSession();
|
|
191
|
-
expect(session?.clientDid).toBe(clientDid);
|
|
192
|
-
|
|
193
|
-
const nonce = await runtime.issueNonce(session!.id);
|
|
194
|
-
session!.nonce = nonce;
|
|
195
|
-
|
|
196
|
-
await runtime.processToolCall(
|
|
197
|
-
"testTool",
|
|
198
|
-
{ test: "data" },
|
|
199
|
-
async () => ({}),
|
|
200
|
-
session
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
const proof = runtime.getLastProof();
|
|
204
|
-
expect(proof).toBeDefined();
|
|
205
|
-
// Client DID should be in proof metadata
|
|
206
|
-
expect((proof as any).clientDid || session?.clientDid).toBe(clientDid);
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe("Tool protection enforcement flow", () => {
|
|
211
|
-
test("should allow unprotected tool calls", async () => {
|
|
212
|
-
// Mock API response with unprotected tool
|
|
213
|
-
const apiResponse = {
|
|
214
|
-
success: true,
|
|
215
|
-
data: {
|
|
216
|
-
toolProtections: {
|
|
217
|
-
search: {
|
|
218
|
-
requiresDelegation: false,
|
|
219
|
-
requiredScopes: [],
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
metadata: {},
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
(global.fetch as any).mockResolvedValueOnce({
|
|
227
|
-
ok: true,
|
|
228
|
-
json: async () => apiResponse,
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
232
|
-
clientDid: "did:key:zclient",
|
|
233
|
-
audience: "https://app.example.com",
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
const session = await runtime.getCurrentSession();
|
|
237
|
-
const nonce = await runtime.issueNonce(session!.id);
|
|
238
|
-
session!.nonce = nonce;
|
|
239
|
-
|
|
240
|
-
// Mock tool protection service
|
|
241
|
-
const protection = await toolProtectionService.checkToolProtection(
|
|
242
|
-
"search",
|
|
243
|
-
mockAgentDid
|
|
244
|
-
);
|
|
245
|
-
expect(protection).toBeNull(); // No protection required
|
|
246
|
-
|
|
247
|
-
// Tool call should proceed normally
|
|
248
|
-
const result = await runtime.processToolCall(
|
|
249
|
-
"search",
|
|
250
|
-
{ query: "test" },
|
|
251
|
-
async (args) => ({ results: [] }),
|
|
252
|
-
session
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
expect(result).toBeDefined();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
test("should intercept protected tool calls without delegation", async () => {
|
|
259
|
-
// Mock API response with protected tool
|
|
260
|
-
const apiResponse = {
|
|
261
|
-
success: true,
|
|
262
|
-
data: {
|
|
263
|
-
toolProtections: {
|
|
264
|
-
checkout: {
|
|
265
|
-
requiresDelegation: true,
|
|
266
|
-
requiredScopes: ["cart:write"],
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
},
|
|
270
|
-
metadata: {},
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
(global.fetch as any).mockResolvedValueOnce({
|
|
274
|
-
ok: true,
|
|
275
|
-
json: async () => apiResponse,
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
279
|
-
clientDid: "did:key:zclient",
|
|
280
|
-
audience: "https://app.example.com",
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const session = await runtime.getCurrentSession();
|
|
284
|
-
const nonce = await runtime.issueNonce(session!.id);
|
|
285
|
-
session!.nonce = nonce;
|
|
286
|
-
|
|
287
|
-
// Check protection
|
|
288
|
-
const protection = await toolProtectionService.checkToolProtection(
|
|
289
|
-
"checkout",
|
|
290
|
-
mockAgentDid
|
|
291
|
-
);
|
|
292
|
-
expect(protection).toBeDefined();
|
|
293
|
-
expect(protection?.requiresDelegation).toBe(true);
|
|
294
|
-
|
|
295
|
-
// Tool call should be intercepted
|
|
296
|
-
// Note: Actual interception happens in runtime.processToolCall
|
|
297
|
-
// This test verifies the protection check works correctly
|
|
298
|
-
expect(protection?.requiredScopes).toContain("cart:write");
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe("Delegation flow (intercept → authorize → resume)", () => {
|
|
303
|
-
test("should store intercepted call for resumption", async () => {
|
|
304
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
305
|
-
clientDid: "did:key:zclient",
|
|
306
|
-
audience: "https://app.example.com",
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
const session = await runtime.getCurrentSession();
|
|
310
|
-
expect(session).toBeDefined();
|
|
311
|
-
|
|
312
|
-
// Simulate intercepted call storage
|
|
313
|
-
const interceptedCall = {
|
|
314
|
-
toolName: "checkout",
|
|
315
|
-
args: { cartId: "123" },
|
|
316
|
-
sessionId: session!.id,
|
|
317
|
-
timestamp: Date.now(),
|
|
318
|
-
expiresAt: Date.now() + 3600000, // 1 hour
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// Verify intercepted call structure
|
|
322
|
-
expect(interceptedCall.toolName).toBe("checkout");
|
|
323
|
-
expect(interceptedCall.args.cartId).toBe("123");
|
|
324
|
-
expect(interceptedCall.sessionId).toBe(session!.id);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
test("should generate resume token for intercepted calls", async () => {
|
|
328
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
329
|
-
clientDid: "did:key:zclient",
|
|
330
|
-
audience: "https://app.example.com",
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
const session = await runtime.getCurrentSession();
|
|
334
|
-
|
|
335
|
-
// Generate resume token (simulated)
|
|
336
|
-
const resumeToken = `resume_${session!.id}_${Date.now()}`;
|
|
337
|
-
|
|
338
|
-
expect(resumeToken).toMatch(/^resume_/);
|
|
339
|
-
expect(resumeToken).toContain(session!.id);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test("should build consent URL with resume token", async () => {
|
|
343
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
344
|
-
clientDid: "did:key:zclient",
|
|
345
|
-
audience: "https://app.example.com",
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
const session = await runtime.getCurrentSession();
|
|
349
|
-
const resumeToken = `resume_${session!.id}_${Date.now()}`;
|
|
350
|
-
|
|
351
|
-
// Build consent URL
|
|
352
|
-
const consentUrl = `https://kya.vouched.id/consent?resume_token=${resumeToken}&tool=checkout&scopes=cart:write`;
|
|
353
|
-
|
|
354
|
-
expect(consentUrl).toContain("resume_token=");
|
|
355
|
-
expect(consentUrl).toContain("tool=checkout");
|
|
356
|
-
expect(consentUrl).toContain("scopes=cart:write");
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
describe("AgentShield integration flow", () => {
|
|
361
|
-
test("should fetch tool protection config from AgentShield merged config", async () => {
|
|
362
|
-
// Merged config format - tools embedded at config.toolProtection.tools
|
|
363
|
-
const apiResponse = {
|
|
364
|
-
success: true,
|
|
365
|
-
data: {
|
|
366
|
-
config: {
|
|
367
|
-
toolProtection: {
|
|
368
|
-
source: 'agentshield',
|
|
369
|
-
tools: {
|
|
370
|
-
protected_tool: {
|
|
371
|
-
requiresDelegation: true,
|
|
372
|
-
requiredScopes: ["scope1"],
|
|
373
|
-
},
|
|
374
|
-
},
|
|
375
|
-
},
|
|
376
|
-
},
|
|
377
|
-
},
|
|
378
|
-
metadata: {},
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
(global.fetch as any).mockResolvedValueOnce({
|
|
382
|
-
ok: true,
|
|
383
|
-
json: async () => apiResponse,
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
const config =
|
|
387
|
-
await toolProtectionService.getToolProtectionConfig(mockAgentDid);
|
|
388
|
-
|
|
389
|
-
expect(config.toolProtections.protected_tool).toBeDefined();
|
|
390
|
-
expect(config.toolProtections.protected_tool.requiresDelegation).toBe(
|
|
391
|
-
true
|
|
392
|
-
);
|
|
393
|
-
// Now calls /config endpoint (not /tool-protections)
|
|
394
|
-
expect(global.fetch).toHaveBeenCalledWith(
|
|
395
|
-
expect.stringContaining(
|
|
396
|
-
"/api/v1/bouncer/projects/test-project/config"
|
|
397
|
-
),
|
|
398
|
-
expect.any(Object)
|
|
399
|
-
);
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
test("should cache tool protection config", async () => {
|
|
403
|
-
// Merged config format - tools embedded at config.toolProtection.tools
|
|
404
|
-
const apiResponse = {
|
|
405
|
-
success: true,
|
|
406
|
-
data: {
|
|
407
|
-
config: {
|
|
408
|
-
toolProtection: {
|
|
409
|
-
source: 'agentshield',
|
|
410
|
-
tools: {
|
|
411
|
-
tool1: {
|
|
412
|
-
requiresDelegation: true,
|
|
413
|
-
requiredScopes: ["scope1"],
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
metadata: {},
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
(global.fetch as any).mockResolvedValueOnce({
|
|
423
|
-
ok: true,
|
|
424
|
-
json: async () => apiResponse,
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
// First call - should fetch from API
|
|
428
|
-
const config1 =
|
|
429
|
-
await toolProtectionService.getToolProtectionConfig(mockAgentDid);
|
|
430
|
-
|
|
431
|
-
// Second call - should use cache
|
|
432
|
-
const config2 =
|
|
433
|
-
await toolProtectionService.getToolProtectionConfig(mockAgentDid);
|
|
434
|
-
|
|
435
|
-
expect(config1).toEqual(config2);
|
|
436
|
-
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
test("should use fallback config when API fails", async () => {
|
|
440
|
-
const fallbackConfig = {
|
|
441
|
-
toolProtections: {
|
|
442
|
-
fallback_tool: {
|
|
443
|
-
requiresDelegation: true,
|
|
444
|
-
requiredScopes: ["fallback"],
|
|
445
|
-
},
|
|
446
|
-
},
|
|
447
|
-
};
|
|
448
|
-
|
|
449
|
-
const toolProtectionConfig: ToolProtectionServiceConfig = {
|
|
450
|
-
apiUrl: "https://kya.vouched.id",
|
|
451
|
-
apiKey: "test-api-key",
|
|
452
|
-
projectId: "test-project",
|
|
453
|
-
cacheTtl: 300000,
|
|
454
|
-
fallbackConfig,
|
|
455
|
-
debug: false,
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const serviceWithFallback = new ToolProtectionService(
|
|
459
|
-
toolProtectionConfig,
|
|
460
|
-
cache
|
|
461
|
-
);
|
|
462
|
-
|
|
463
|
-
(global.fetch as any).mockRejectedValueOnce(new Error("Network error"));
|
|
464
|
-
|
|
465
|
-
const result =
|
|
466
|
-
await serviceWithFallback.getToolProtectionConfig(mockAgentDid);
|
|
467
|
-
|
|
468
|
-
expect(result).toEqual(fallbackConfig);
|
|
469
|
-
});
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
describe("Session expiry handling", () => {
|
|
473
|
-
test("should handle expired sessions correctly", async () => {
|
|
474
|
-
const clock = mockProviders.clockProvider as MockClockProvider;
|
|
475
|
-
const currentTime = Date.now();
|
|
476
|
-
clock.setTime(currentTime);
|
|
477
|
-
|
|
478
|
-
await runtime.handleHandshake({
|
|
479
|
-
clientDid: "did:key:zclient",
|
|
480
|
-
audience: "https://app.example.com",
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
let session = await runtime.getCurrentSession();
|
|
484
|
-
expect(session).toBeDefined();
|
|
485
|
-
|
|
486
|
-
// Advance time past expiry (30 minutes + 1 second)
|
|
487
|
-
clock.setTime(currentTime + 30 * 60 * 1000 + 1000);
|
|
488
|
-
|
|
489
|
-
session = await runtime.getCurrentSession();
|
|
490
|
-
expect(session).toBeNull();
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
test("should reject tool calls with expired session", async () => {
|
|
494
|
-
const clock = mockProviders.clockProvider as MockClockProvider;
|
|
495
|
-
const currentTime = Date.now();
|
|
496
|
-
clock.setTime(currentTime);
|
|
497
|
-
|
|
498
|
-
await runtime.handleHandshake({
|
|
499
|
-
clientDid: "did:key:zclient",
|
|
500
|
-
audience: "https://app.example.com",
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
const session = await runtime.getCurrentSession();
|
|
504
|
-
expect(session).toBeDefined();
|
|
505
|
-
|
|
506
|
-
// Advance time past expiry
|
|
507
|
-
clock.setTime(currentTime + 30 * 60 * 1000 + 1000);
|
|
508
|
-
|
|
509
|
-
// Session should be expired
|
|
510
|
-
const expiredSession = await runtime.getCurrentSession();
|
|
511
|
-
expect(expiredSession).toBeNull();
|
|
512
|
-
});
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
describe("Error handling in full flow", () => {
|
|
516
|
-
test("should handle tool protection service errors gracefully", async () => {
|
|
517
|
-
// Mock API failure
|
|
518
|
-
(global.fetch as any).mockRejectedValueOnce(new Error("Network error"));
|
|
519
|
-
|
|
520
|
-
// Should fail-closed (deny-all) by default
|
|
521
|
-
const config =
|
|
522
|
-
await toolProtectionService.getToolProtectionConfig(mockAgentDid);
|
|
523
|
-
|
|
524
|
-
expect(config.toolProtections['*']).toBeDefined();
|
|
525
|
-
expect(config.toolProtections['*']?.requiresDelegation).toBe(true);
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
test("should handle invalid API responses", async () => {
|
|
529
|
-
(global.fetch as any).mockResolvedValueOnce({
|
|
530
|
-
ok: true,
|
|
531
|
-
json: async () => ({
|
|
532
|
-
success: false,
|
|
533
|
-
error: "Invalid request",
|
|
534
|
-
}),
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
await expect(
|
|
538
|
-
toolProtectionService.getToolProtectionConfig(mockAgentDid)
|
|
539
|
-
).rejects.toThrow("API returned success: false");
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
test("should handle network timeouts", async () => {
|
|
543
|
-
const fallbackConfig = {
|
|
544
|
-
toolProtections: {
|
|
545
|
-
tool1: {
|
|
546
|
-
requiresDelegation: true,
|
|
547
|
-
requiredScopes: ["scope1"],
|
|
548
|
-
},
|
|
549
|
-
},
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
const toolProtectionConfig: ToolProtectionServiceConfig = {
|
|
553
|
-
apiUrl: "https://kya.vouched.id",
|
|
554
|
-
apiKey: "test-api-key",
|
|
555
|
-
projectId: "test-project",
|
|
556
|
-
cacheTtl: 300000,
|
|
557
|
-
fallbackConfig,
|
|
558
|
-
debug: false,
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
const serviceWithFallback = new ToolProtectionService(
|
|
562
|
-
toolProtectionConfig,
|
|
563
|
-
cache
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
(global.fetch as any).mockRejectedValueOnce(new Error("Network timeout"));
|
|
567
|
-
|
|
568
|
-
const result =
|
|
569
|
-
await serviceWithFallback.getToolProtectionConfig(mockAgentDid);
|
|
570
|
-
|
|
571
|
-
expect(result).toEqual(fallbackConfig);
|
|
572
|
-
});
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
describe("Cache integration in full flow", () => {
|
|
576
|
-
test("should share cache across multiple service instances", async () => {
|
|
577
|
-
const apiResponse = {
|
|
578
|
-
success: true,
|
|
579
|
-
data: {
|
|
580
|
-
toolProtections: {
|
|
581
|
-
tool1: {
|
|
582
|
-
requiresDelegation: true,
|
|
583
|
-
requiredScopes: ["scope1"],
|
|
584
|
-
},
|
|
585
|
-
},
|
|
586
|
-
},
|
|
587
|
-
metadata: {},
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
(global.fetch as any).mockResolvedValueOnce({
|
|
591
|
-
ok: true,
|
|
592
|
-
json: async () => apiResponse,
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
const service1 = new ToolProtectionService(
|
|
596
|
-
{
|
|
597
|
-
apiUrl: "https://kya.vouched.id",
|
|
598
|
-
apiKey: "test-api-key",
|
|
599
|
-
projectId: "test-project",
|
|
600
|
-
cacheTtl: 300000,
|
|
601
|
-
},
|
|
602
|
-
cache
|
|
603
|
-
);
|
|
604
|
-
|
|
605
|
-
const service2 = new ToolProtectionService(
|
|
606
|
-
{
|
|
607
|
-
apiUrl: "https://kya.vouched.id",
|
|
608
|
-
apiKey: "test-api-key",
|
|
609
|
-
projectId: "test-project",
|
|
610
|
-
cacheTtl: 300000,
|
|
611
|
-
},
|
|
612
|
-
cache
|
|
613
|
-
);
|
|
614
|
-
|
|
615
|
-
// First service fetches from API
|
|
616
|
-
await service1.getToolProtectionConfig(mockAgentDid);
|
|
617
|
-
|
|
618
|
-
// Second service should use cached value
|
|
619
|
-
const config2 = await service2.getToolProtectionConfig(mockAgentDid);
|
|
620
|
-
|
|
621
|
-
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
622
|
-
expect(config2.toolProtections.tool1).toBeDefined();
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
test("should clear cache when needed", async () => {
|
|
626
|
-
const apiResponse = {
|
|
627
|
-
success: true,
|
|
628
|
-
data: {
|
|
629
|
-
toolProtections: {
|
|
630
|
-
tool1: {
|
|
631
|
-
requiresDelegation: true,
|
|
632
|
-
requiredScopes: ["scope1"],
|
|
633
|
-
},
|
|
634
|
-
},
|
|
635
|
-
},
|
|
636
|
-
metadata: {},
|
|
637
|
-
};
|
|
638
|
-
|
|
639
|
-
(global.fetch as any).mockResolvedValue({
|
|
640
|
-
ok: true,
|
|
641
|
-
json: async () => apiResponse,
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
// Fetch and cache
|
|
645
|
-
await toolProtectionService.getToolProtectionConfig(mockAgentDid);
|
|
646
|
-
|
|
647
|
-
// Clear cache
|
|
648
|
-
await toolProtectionService.clearCache(mockAgentDid);
|
|
649
|
-
|
|
650
|
-
// Next fetch should go to API again
|
|
651
|
-
await toolProtectionService.getToolProtectionConfig(mockAgentDid);
|
|
652
|
-
|
|
653
|
-
expect(global.fetch).toHaveBeenCalledTimes(2);
|
|
654
|
-
});
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
describe("Real-world e-commerce scenario", () => {
|
|
658
|
-
test("should handle complete e-commerce flow with tool protection", async () => {
|
|
659
|
-
// Step 1: Set up tool protection config
|
|
660
|
-
const apiResponse = {
|
|
661
|
-
success: true,
|
|
662
|
-
data: {
|
|
663
|
-
toolProtections: {
|
|
664
|
-
search_products: {
|
|
665
|
-
requiresDelegation: false,
|
|
666
|
-
requiredScopes: [],
|
|
667
|
-
},
|
|
668
|
-
add_to_cart: {
|
|
669
|
-
requiresDelegation: true,
|
|
670
|
-
requiredScopes: ["cart:write"],
|
|
671
|
-
},
|
|
672
|
-
checkout: {
|
|
673
|
-
requiresDelegation: true,
|
|
674
|
-
requiredScopes: ["cart:write", "payment:process"],
|
|
675
|
-
},
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
metadata: {},
|
|
679
|
-
};
|
|
680
|
-
|
|
681
|
-
(global.fetch as any).mockResolvedValueOnce({
|
|
682
|
-
ok: true,
|
|
683
|
-
json: async () => apiResponse,
|
|
684
|
-
});
|
|
685
|
-
|
|
686
|
-
// Step 2: Handshake
|
|
687
|
-
const handshakeResponse = await runtime.handleHandshake({
|
|
688
|
-
clientDid: "did:key:zclient",
|
|
689
|
-
audience: "https://shop.example.com",
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
const session = await runtime.getCurrentSession();
|
|
693
|
-
expect(session).toBeDefined();
|
|
694
|
-
|
|
695
|
-
// Step 3: Unprotected tool call (search)
|
|
696
|
-
const nonce1 = await runtime.issueNonce(session!.id);
|
|
697
|
-
session!.nonce = nonce1;
|
|
698
|
-
|
|
699
|
-
const searchProtection = await toolProtectionService.checkToolProtection(
|
|
700
|
-
"search_products",
|
|
701
|
-
mockAgentDid
|
|
702
|
-
);
|
|
703
|
-
expect(searchProtection).toBeNull(); // No protection
|
|
704
|
-
|
|
705
|
-
const searchResult = await runtime.processToolCall(
|
|
706
|
-
"search_products",
|
|
707
|
-
{ query: "laptop" },
|
|
708
|
-
async () => ({ products: [] }),
|
|
709
|
-
session
|
|
710
|
-
);
|
|
711
|
-
|
|
712
|
-
expect(searchResult).toBeDefined();
|
|
713
|
-
|
|
714
|
-
// Step 4: Check protection for add_to_cart (should require delegation)
|
|
715
|
-
const nonce2 = await runtime.issueNonce(session!.id);
|
|
716
|
-
session!.nonce = nonce2;
|
|
717
|
-
|
|
718
|
-
const cartProtection = await toolProtectionService.checkToolProtection(
|
|
719
|
-
"add_to_cart",
|
|
720
|
-
mockAgentDid
|
|
721
|
-
);
|
|
722
|
-
|
|
723
|
-
expect(cartProtection).toBeDefined();
|
|
724
|
-
expect(cartProtection?.requiresDelegation).toBe(true);
|
|
725
|
-
expect(cartProtection?.requiredScopes).toContain("cart:write");
|
|
726
|
-
|
|
727
|
-
// Step 5: Verify proof was created for search tool call (not for protection check)
|
|
728
|
-
const proof = runtime.getLastProof();
|
|
729
|
-
expect(proof).toBeDefined();
|
|
730
|
-
expect(proof?.nonce).toBe(nonce1); // Proof from search_products call
|
|
731
|
-
});
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
describe("Concurrent operations", () => {
|
|
735
|
-
test("should handle concurrent tool calls", async () => {
|
|
736
|
-
await runtime.handleHandshake({
|
|
737
|
-
clientDid: "did:key:zclient",
|
|
738
|
-
audience: "https://app.example.com",
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
const session = await runtime.getCurrentSession();
|
|
742
|
-
expect(session).toBeDefined();
|
|
743
|
-
|
|
744
|
-
// Issue multiple nonces
|
|
745
|
-
const nonces = await Promise.all([
|
|
746
|
-
runtime.issueNonce(session!.id),
|
|
747
|
-
runtime.issueNonce(session!.id),
|
|
748
|
-
runtime.issueNonce(session!.id),
|
|
749
|
-
]);
|
|
750
|
-
|
|
751
|
-
expect(nonces).toHaveLength(3);
|
|
752
|
-
expect(new Set(nonces).size).toBe(3); // All unique
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
test("should handle concurrent cache operations", async () => {
|
|
756
|
-
const apiResponse = {
|
|
757
|
-
success: true,
|
|
758
|
-
data: {
|
|
759
|
-
toolProtections: {
|
|
760
|
-
tool1: {
|
|
761
|
-
requiresDelegation: true,
|
|
762
|
-
requiredScopes: ["scope1"],
|
|
763
|
-
},
|
|
764
|
-
},
|
|
765
|
-
},
|
|
766
|
-
metadata: {},
|
|
767
|
-
};
|
|
768
|
-
|
|
769
|
-
(global.fetch as any).mockResolvedValue({
|
|
770
|
-
ok: true,
|
|
771
|
-
json: async () => apiResponse,
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
// Concurrent config fetches
|
|
775
|
-
const promises = [
|
|
776
|
-
toolProtectionService.getToolProtectionConfig(mockAgentDid),
|
|
777
|
-
toolProtectionService.getToolProtectionConfig(mockAgentDid),
|
|
778
|
-
toolProtectionService.getToolProtectionConfig(mockAgentDid),
|
|
779
|
-
];
|
|
780
|
-
|
|
781
|
-
const results = await Promise.all(promises);
|
|
782
|
-
|
|
783
|
-
// All should return same config
|
|
784
|
-
results.forEach((result) => {
|
|
785
|
-
expect(result.toolProtections.tool1).toBeDefined();
|
|
786
|
-
});
|
|
787
|
-
});
|
|
788
|
-
});
|
|
789
|
-
});
|