@kya-os/mcp-i 1.2.1 → 1.2.3
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/149.js +1 -1
- package/dist/261.js +1 -1
- package/dist/742.js +1 -1
- package/dist/904.js +1 -1
- package/dist/cli-adapter/index.d.ts +54 -0
- package/dist/cli-adapter/index.js +84 -0
- package/dist/cli-adapter/kta-registration.d.ts +39 -0
- package/dist/cli-adapter/kta-registration.js +92 -0
- package/dist/compiler/get-webpack-config/get-externals.js +2 -2
- package/dist/compiler/get-webpack-config/plugins.js +1 -13
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.LICENSE.txt +14 -0
- package/dist/runtime/session.js +4 -2
- package/dist/storage/encryption.d.ts +61 -0
- package/dist/storage/encryption.js +151 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.js +26 -0
- package/package.json +2 -2
- package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.d.ts +0 -4
- package/dist/cache/__tests__/cloudflare-kv-nonce-cache.test.js +0 -176
- package/dist/cache/__tests__/concurrency.test.d.ts +0 -5
- package/dist/cache/__tests__/concurrency.test.js +0 -300
- package/dist/cache/__tests__/dynamodb-nonce-cache.test.d.ts +0 -4
- package/dist/cache/__tests__/dynamodb-nonce-cache.test.js +0 -176
- package/dist/cache/__tests__/memory-nonce-cache.test.d.ts +0 -4
- package/dist/cache/__tests__/memory-nonce-cache.test.js +0 -132
- package/dist/cache/__tests__/nonce-cache-factory-simple.test.d.ts +0 -4
- package/dist/cache/__tests__/nonce-cache-factory-simple.test.js +0 -133
- package/dist/cache/__tests__/nonce-cache-factory.test.d.ts +0 -4
- package/dist/cache/__tests__/nonce-cache-factory.test.js +0 -252
- package/dist/cache/__tests__/redis-nonce-cache.test.d.ts +0 -4
- package/dist/cache/__tests__/redis-nonce-cache.test.js +0 -95
- package/dist/runtime/__tests__/audit.test.d.ts +0 -4
- package/dist/runtime/__tests__/audit.test.js +0 -328
- package/dist/runtime/__tests__/identity.test.d.ts +0 -4
- package/dist/runtime/__tests__/identity.test.js +0 -164
- package/dist/runtime/__tests__/mcpi-runtime.test.d.ts +0 -4
- package/dist/runtime/__tests__/mcpi-runtime.test.js +0 -372
- package/dist/runtime/__tests__/proof.test.d.ts +0 -4
- package/dist/runtime/__tests__/proof.test.js +0 -302
- package/dist/runtime/__tests__/session.test.d.ts +0 -4
- package/dist/runtime/__tests__/session.test.js +0 -254
- package/dist/runtime/__tests__/well-known.test.d.ts +0 -4
- package/dist/runtime/__tests__/well-known.test.js +0 -312
- package/dist/test/__tests__/nonce-cache-integration.test.d.ts +0 -1
- package/dist/test/__tests__/nonce-cache-integration.test.js +0 -116
- package/dist/test/__tests__/nonce-cache.test.d.ts +0 -1
- package/dist/test/__tests__/nonce-cache.test.js +0 -122
- package/dist/test/__tests__/runtime-integration.test.d.ts +0 -4
- package/dist/test/__tests__/runtime-integration.test.js +0 -192
- package/dist/test/__tests__/test-infrastructure.test.d.ts +0 -4
- package/dist/test/__tests__/test-infrastructure.test.js +0 -178
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Concurrency tests for nonce cache implementations
|
|
4
|
-
* Tests multi-instance replay prevention and atomic operations
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const vitest_1 = require("vitest");
|
|
8
|
-
const memory_nonce_cache_js_1 = require("../memory-nonce-cache.js");
|
|
9
|
-
const redis_nonce_cache_js_1 = require("../redis-nonce-cache.js");
|
|
10
|
-
const dynamodb_nonce_cache_js_1 = require("../dynamodb-nonce-cache.js");
|
|
11
|
-
const cloudflare_kv_nonce_cache_js_1 = require("../cloudflare-kv-nonce-cache.js");
|
|
12
|
-
// Mock implementations for external services
|
|
13
|
-
const mockRedis = {
|
|
14
|
-
exists: vitest_1.vi.fn(),
|
|
15
|
-
set: vitest_1.vi.fn(),
|
|
16
|
-
};
|
|
17
|
-
const mockDynamoDB = {
|
|
18
|
-
getItem: vitest_1.vi.fn(),
|
|
19
|
-
putItem: vitest_1.vi.fn(),
|
|
20
|
-
};
|
|
21
|
-
const mockKV = {
|
|
22
|
-
get: vitest_1.vi.fn(),
|
|
23
|
-
getWithMetadata: vitest_1.vi.fn(),
|
|
24
|
-
put: vitest_1.vi.fn(),
|
|
25
|
-
delete: vitest_1.vi.fn(),
|
|
26
|
-
};
|
|
27
|
-
(0, vitest_1.describe)("Nonce Cache Concurrency Tests", () => {
|
|
28
|
-
let memoryCache;
|
|
29
|
-
let redisCache;
|
|
30
|
-
let dynamoCache;
|
|
31
|
-
let kvCache;
|
|
32
|
-
(0, vitest_1.beforeEach)(() => {
|
|
33
|
-
vitest_1.vi.clearAllMocks();
|
|
34
|
-
memoryCache = new memory_nonce_cache_js_1.MemoryNonceCache(100);
|
|
35
|
-
redisCache = new redis_nonce_cache_js_1.RedisNonceCache(mockRedis, "test:");
|
|
36
|
-
dynamoCache = new dynamodb_nonce_cache_js_1.DynamoNonceCache(mockDynamoDB, "test-table");
|
|
37
|
-
kvCache = new cloudflare_kv_nonce_cache_js_1.CloudflareKVNonceCache(mockKV, "test:");
|
|
38
|
-
});
|
|
39
|
-
(0, vitest_1.afterEach)(() => {
|
|
40
|
-
memoryCache.destroy();
|
|
41
|
-
});
|
|
42
|
-
(0, vitest_1.describe)("Memory Cache Concurrency", () => {
|
|
43
|
-
(0, vitest_1.it)("should prevent concurrent duplicate nonce addition", async () => {
|
|
44
|
-
const nonce = "concurrent-memory-nonce";
|
|
45
|
-
const ttl = 60;
|
|
46
|
-
// Simulate concurrent add operations
|
|
47
|
-
const promises = [
|
|
48
|
-
memoryCache.add(nonce, ttl),
|
|
49
|
-
memoryCache.add(nonce, ttl),
|
|
50
|
-
memoryCache.add(nonce, ttl),
|
|
51
|
-
];
|
|
52
|
-
const results = await Promise.allSettled(promises);
|
|
53
|
-
// Exactly one should succeed, others should fail
|
|
54
|
-
const successful = results.filter((r) => r.status === "fulfilled");
|
|
55
|
-
const failed = results.filter((r) => r.status === "rejected");
|
|
56
|
-
(0, vitest_1.expect)(successful).toHaveLength(1);
|
|
57
|
-
(0, vitest_1.expect)(failed).toHaveLength(2);
|
|
58
|
-
// All failures should be due to duplicate nonce
|
|
59
|
-
failed.forEach((result) => {
|
|
60
|
-
if (result.status === "rejected") {
|
|
61
|
-
(0, vitest_1.expect)(result.reason.message).toContain("already exists");
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
// Nonce should exist after successful addition
|
|
65
|
-
(0, vitest_1.expect)(await memoryCache.has(nonce)).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
(0, vitest_1.it)("should handle rapid sequential operations", async () => {
|
|
68
|
-
const baseNonce = "rapid-memory-nonce";
|
|
69
|
-
const operations = [];
|
|
70
|
-
// Create 100 rapid sequential operations
|
|
71
|
-
for (let i = 0; i < 100; i++) {
|
|
72
|
-
operations.push(memoryCache.add(`${baseNonce}-${i}`, 60));
|
|
73
|
-
}
|
|
74
|
-
// All should succeed since they're different nonces
|
|
75
|
-
const results = await Promise.allSettled(operations);
|
|
76
|
-
const successful = results.filter((r) => r.status === "fulfilled");
|
|
77
|
-
(0, vitest_1.expect)(successful).toHaveLength(100);
|
|
78
|
-
// All nonces should exist
|
|
79
|
-
for (let i = 0; i < 100; i++) {
|
|
80
|
-
(0, vitest_1.expect)(await memoryCache.has(`${baseNonce}-${i}`)).toBe(true);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
(0, vitest_1.describe)("Redis Cache Atomicity", () => {
|
|
85
|
-
(0, vitest_1.it)("should use atomic SET NX EX for add operations", async () => {
|
|
86
|
-
const nonce = "atomic-redis-nonce";
|
|
87
|
-
const ttl = 300;
|
|
88
|
-
// Mock successful atomic operation
|
|
89
|
-
mockRedis.set.mockResolvedValue("OK");
|
|
90
|
-
await redisCache.add(nonce, ttl);
|
|
91
|
-
// Verify atomic command was used
|
|
92
|
-
(0, vitest_1.expect)(mockRedis.set).toHaveBeenCalledWith("test:atomic-redis-nonce", "1", "EX", ttl, "NX");
|
|
93
|
-
});
|
|
94
|
-
(0, vitest_1.it)("should handle concurrent add attempts atomically", async () => {
|
|
95
|
-
const nonce = "concurrent-redis-nonce";
|
|
96
|
-
// First call succeeds, subsequent calls fail
|
|
97
|
-
mockRedis.set.mockResolvedValueOnce("OK").mockResolvedValue(null);
|
|
98
|
-
const promises = [
|
|
99
|
-
redisCache.add(nonce, 60),
|
|
100
|
-
redisCache.add(nonce, 60),
|
|
101
|
-
redisCache.add(nonce, 60),
|
|
102
|
-
];
|
|
103
|
-
const results = await Promise.allSettled(promises);
|
|
104
|
-
// First should succeed, others should fail
|
|
105
|
-
(0, vitest_1.expect)(results[0].status).toBe("fulfilled");
|
|
106
|
-
(0, vitest_1.expect)(results[1].status).toBe("rejected");
|
|
107
|
-
(0, vitest_1.expect)(results[2].status).toBe("rejected");
|
|
108
|
-
// Verify all calls used atomic operation
|
|
109
|
-
(0, vitest_1.expect)(mockRedis.set).toHaveBeenCalledTimes(3);
|
|
110
|
-
mockRedis.set.mock.calls.forEach((call) => {
|
|
111
|
-
(0, vitest_1.expect)(call).toEqual([
|
|
112
|
-
vitest_1.expect.stringContaining(nonce),
|
|
113
|
-
"1",
|
|
114
|
-
"EX",
|
|
115
|
-
60,
|
|
116
|
-
"NX",
|
|
117
|
-
]);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
(0, vitest_1.describe)("DynamoDB Cache Atomicity", () => {
|
|
122
|
-
(0, vitest_1.it)("should use conditional writes for atomicity", async () => {
|
|
123
|
-
const nonce = "atomic-dynamo-nonce";
|
|
124
|
-
const ttl = 300;
|
|
125
|
-
// Mock successful conditional write
|
|
126
|
-
mockDynamoDB.putItem.mockReturnValue({
|
|
127
|
-
promise: () => Promise.resolve({}),
|
|
128
|
-
});
|
|
129
|
-
await dynamoCache.add(nonce, ttl);
|
|
130
|
-
// Verify conditional expression was used
|
|
131
|
-
(0, vitest_1.expect)(mockDynamoDB.putItem).toHaveBeenCalledWith({
|
|
132
|
-
TableName: "test-table",
|
|
133
|
-
Item: {
|
|
134
|
-
nonce: { S: nonce },
|
|
135
|
-
expiresAt: { N: vitest_1.expect.any(String) },
|
|
136
|
-
createdAt: { N: vitest_1.expect.any(String) },
|
|
137
|
-
},
|
|
138
|
-
ConditionExpression: "attribute_not_exists(nonce)",
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
(0, vitest_1.it)("should handle concurrent add attempts with conditional writes", async () => {
|
|
142
|
-
const nonce = "concurrent-dynamo-nonce";
|
|
143
|
-
// First call succeeds
|
|
144
|
-
mockDynamoDB.putItem.mockReturnValueOnce({
|
|
145
|
-
promise: () => Promise.resolve({}),
|
|
146
|
-
});
|
|
147
|
-
// Subsequent calls fail with conditional check exception
|
|
148
|
-
const conditionalError = new Error("ConditionalCheckFailedException");
|
|
149
|
-
conditionalError.code = "ConditionalCheckFailedException";
|
|
150
|
-
mockDynamoDB.putItem.mockReturnValue({
|
|
151
|
-
promise: () => Promise.reject(conditionalError),
|
|
152
|
-
});
|
|
153
|
-
const promises = [
|
|
154
|
-
dynamoCache.add(nonce, 60),
|
|
155
|
-
dynamoCache.add(nonce, 60),
|
|
156
|
-
dynamoCache.add(nonce, 60),
|
|
157
|
-
];
|
|
158
|
-
const results = await Promise.allSettled(promises);
|
|
159
|
-
// First should succeed, others should fail
|
|
160
|
-
(0, vitest_1.expect)(results[0].status).toBe("fulfilled");
|
|
161
|
-
(0, vitest_1.expect)(results[1].status).toBe("rejected");
|
|
162
|
-
(0, vitest_1.expect)(results[2].status).toBe("rejected");
|
|
163
|
-
// All failures should be due to conditional check
|
|
164
|
-
results.slice(1).forEach((result) => {
|
|
165
|
-
if (result.status === "rejected") {
|
|
166
|
-
(0, vitest_1.expect)(result.reason.message).toContain("already exists");
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
(0, vitest_1.describe)("Cloudflare KV Cache Best-Effort Atomicity", () => {
|
|
172
|
-
(0, vitest_1.it)("should attempt atomicity with getWithMetadata", async () => {
|
|
173
|
-
const nonce = "atomic-kv-nonce";
|
|
174
|
-
// Mock no existing value
|
|
175
|
-
mockKV.getWithMetadata.mockResolvedValue({ value: null, metadata: null });
|
|
176
|
-
mockKV.put.mockResolvedValue(undefined);
|
|
177
|
-
await kvCache.add(nonce, 60);
|
|
178
|
-
(0, vitest_1.expect)(mockKV.getWithMetadata).toHaveBeenCalledWith("test:atomic-kv-nonce");
|
|
179
|
-
(0, vitest_1.expect)(mockKV.put).toHaveBeenCalled();
|
|
180
|
-
});
|
|
181
|
-
(0, vitest_1.it)("should detect existing nonces with getWithMetadata", async () => {
|
|
182
|
-
const nonce = "existing-kv-nonce";
|
|
183
|
-
// Mock existing valid nonce
|
|
184
|
-
const futureTime = Date.now() + 50000;
|
|
185
|
-
mockKV.getWithMetadata.mockResolvedValue({
|
|
186
|
-
value: JSON.stringify({
|
|
187
|
-
nonce,
|
|
188
|
-
expiresAt: futureTime,
|
|
189
|
-
createdAt: Date.now(),
|
|
190
|
-
}),
|
|
191
|
-
metadata: null,
|
|
192
|
-
});
|
|
193
|
-
await (0, vitest_1.expect)(kvCache.add(nonce, 60)).rejects.toThrow("already exists - potential replay attack");
|
|
194
|
-
});
|
|
195
|
-
(0, vitest_1.it)("should fall back to basic operations when getWithMetadata unavailable", async () => {
|
|
196
|
-
const nonce = "fallback-kv-nonce";
|
|
197
|
-
// Mock getWithMetadata failure
|
|
198
|
-
mockKV.getWithMetadata.mockRejectedValue(new Error("getWithMetadata is not available"));
|
|
199
|
-
// Mock basic operations
|
|
200
|
-
mockKV.get.mockResolvedValue(null);
|
|
201
|
-
mockKV.put.mockResolvedValue(undefined);
|
|
202
|
-
await kvCache.add(nonce, 60);
|
|
203
|
-
// Should fall back to basic has() check
|
|
204
|
-
(0, vitest_1.expect)(mockKV.get).toHaveBeenCalledWith("test:fallback-kv-nonce");
|
|
205
|
-
(0, vitest_1.expect)(mockKV.put).toHaveBeenCalled();
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
(0, vitest_1.describe)("Cross-Implementation Consistency", () => {
|
|
209
|
-
const testCases = [
|
|
210
|
-
{ name: "Memory", cache: () => memoryCache },
|
|
211
|
-
{
|
|
212
|
-
name: "Redis",
|
|
213
|
-
cache: () => redisCache,
|
|
214
|
-
setup: () => {
|
|
215
|
-
mockRedis.exists.mockResolvedValue(0);
|
|
216
|
-
mockRedis.set.mockResolvedValue("OK");
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
name: "DynamoDB",
|
|
221
|
-
cache: () => dynamoCache,
|
|
222
|
-
setup: () => {
|
|
223
|
-
mockDynamoDB.getItem.mockReturnValue({
|
|
224
|
-
promise: () => Promise.resolve({}),
|
|
225
|
-
});
|
|
226
|
-
mockDynamoDB.putItem.mockReturnValue({
|
|
227
|
-
promise: () => Promise.resolve({}),
|
|
228
|
-
});
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
name: "Cloudflare KV",
|
|
233
|
-
cache: () => kvCache,
|
|
234
|
-
setup: () => {
|
|
235
|
-
mockKV.get.mockResolvedValue(null);
|
|
236
|
-
mockKV.getWithMetadata.mockResolvedValue({
|
|
237
|
-
value: null,
|
|
238
|
-
metadata: null,
|
|
239
|
-
});
|
|
240
|
-
mockKV.put.mockResolvedValue(undefined);
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
];
|
|
244
|
-
testCases.forEach(({ name, cache, setup }) => {
|
|
245
|
-
(0, vitest_1.it)(`${name} should implement consistent interface`, async () => {
|
|
246
|
-
if (setup)
|
|
247
|
-
setup();
|
|
248
|
-
const cacheInstance = cache();
|
|
249
|
-
const nonce = `interface-test-${name.toLowerCase()}-nonce`;
|
|
250
|
-
// Test interface methods exist and work
|
|
251
|
-
(0, vitest_1.expect)(typeof cacheInstance.has).toBe("function");
|
|
252
|
-
(0, vitest_1.expect)(typeof cacheInstance.add).toBe("function");
|
|
253
|
-
(0, vitest_1.expect)(typeof cacheInstance.cleanup).toBe("function");
|
|
254
|
-
// Test basic flow
|
|
255
|
-
if (name === "Memory") {
|
|
256
|
-
// Only test actual functionality for memory cache
|
|
257
|
-
(0, vitest_1.expect)(await cacheInstance.has(nonce)).toBe(false);
|
|
258
|
-
await cacheInstance.add(nonce, 60);
|
|
259
|
-
(0, vitest_1.expect)(await cacheInstance.has(nonce)).toBe(true);
|
|
260
|
-
}
|
|
261
|
-
// cleanup should not throw
|
|
262
|
-
await (0, vitest_1.expect)(cacheInstance.cleanup()).resolves.not.toThrow();
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
(0, vitest_1.describe)("Performance and Stress Testing", () => {
|
|
267
|
-
(0, vitest_1.it)("should handle high-frequency operations on memory cache", async () => {
|
|
268
|
-
const startTime = Date.now();
|
|
269
|
-
const operations = [];
|
|
270
|
-
// Create 1000 rapid operations
|
|
271
|
-
for (let i = 0; i < 1000; i++) {
|
|
272
|
-
operations.push(memoryCache.add(`stress-nonce-${i}`, 60));
|
|
273
|
-
}
|
|
274
|
-
await Promise.all(operations);
|
|
275
|
-
const endTime = Date.now();
|
|
276
|
-
// Should complete within reasonable time (adjust threshold as needed)
|
|
277
|
-
(0, vitest_1.expect)(endTime - startTime).toBeLessThan(1000); // 1 second
|
|
278
|
-
// All nonces should exist
|
|
279
|
-
for (let i = 0; i < 1000; i++) {
|
|
280
|
-
(0, vitest_1.expect)(await memoryCache.has(`stress-nonce-${i}`)).toBe(true);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
(0, vitest_1.it)("should handle mixed read/write operations on memory cache", async () => {
|
|
284
|
-
const nonce = "mixed-ops-nonce";
|
|
285
|
-
// Add initial nonce
|
|
286
|
-
await memoryCache.add(nonce, 60);
|
|
287
|
-
// Create mixed operations
|
|
288
|
-
const operations = [];
|
|
289
|
-
for (let i = 0; i < 100; i++) {
|
|
290
|
-
operations.push(memoryCache.has(nonce));
|
|
291
|
-
operations.push(memoryCache.add(`mixed-${i}`, 60));
|
|
292
|
-
}
|
|
293
|
-
const results = await Promise.allSettled(operations);
|
|
294
|
-
// All has() operations should succeed
|
|
295
|
-
// All add() operations should succeed (different nonces)
|
|
296
|
-
const successful = results.filter((r) => r.status === "fulfilled");
|
|
297
|
-
(0, vitest_1.expect)(successful.length).toBeGreaterThan(150); // Most should succeed
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Tests for DynamoDB Nonce Cache
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const vitest_1 = require("vitest");
|
|
7
|
-
const dynamodb_nonce_cache_js_1 = require("../dynamodb-nonce-cache.js");
|
|
8
|
-
// Mock DynamoDB client
|
|
9
|
-
const mockDynamoDB = {
|
|
10
|
-
getItem: vitest_1.vi.fn(),
|
|
11
|
-
putItem: vitest_1.vi.fn(),
|
|
12
|
-
};
|
|
13
|
-
// Helper to create mock DynamoDB responses
|
|
14
|
-
const createMockGetItemResponse = (exists, expired = false) => {
|
|
15
|
-
if (!exists) {
|
|
16
|
-
return { promise: () => Promise.resolve({}) };
|
|
17
|
-
}
|
|
18
|
-
const expiresAt = expired
|
|
19
|
-
? Math.floor(Date.now() / 1000) - 100 // Expired 100 seconds ago
|
|
20
|
-
: Math.floor(Date.now() / 1000) + 300; // Expires in 300 seconds
|
|
21
|
-
return {
|
|
22
|
-
promise: () => Promise.resolve({
|
|
23
|
-
Item: {
|
|
24
|
-
nonce: { S: "test-nonce" },
|
|
25
|
-
expiresAt: { N: expiresAt.toString() },
|
|
26
|
-
},
|
|
27
|
-
}),
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
const createMockPutItemResponse = (success = true) => {
|
|
31
|
-
if (success) {
|
|
32
|
-
return { promise: () => Promise.resolve({}) };
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
const error = new Error("ConditionalCheckFailedException");
|
|
36
|
-
error.code = "ConditionalCheckFailedException";
|
|
37
|
-
return { promise: () => Promise.reject(error) };
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
(0, vitest_1.describe)("DynamoNonceCache", () => {
|
|
41
|
-
let cache;
|
|
42
|
-
(0, vitest_1.beforeEach)(() => {
|
|
43
|
-
vitest_1.vi.clearAllMocks();
|
|
44
|
-
cache = new dynamodb_nonce_cache_js_1.DynamoNonceCache(mockDynamoDB, "test-nonce-table");
|
|
45
|
-
});
|
|
46
|
-
(0, vitest_1.describe)("Basic Operations", () => {
|
|
47
|
-
(0, vitest_1.it)("should add and check nonce existence", async () => {
|
|
48
|
-
const nonce = "test-nonce-123";
|
|
49
|
-
// Mock DynamoDB responses
|
|
50
|
-
mockDynamoDB.getItem.mockReturnValue(createMockGetItemResponse(false));
|
|
51
|
-
mockDynamoDB.putItem.mockReturnValue(createMockPutItemResponse(true));
|
|
52
|
-
// Initially should not exist
|
|
53
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
54
|
-
(0, vitest_1.expect)(mockDynamoDB.getItem).toHaveBeenCalledWith({
|
|
55
|
-
TableName: "test-nonce-table",
|
|
56
|
-
Key: { nonce: { S: nonce } },
|
|
57
|
-
ConsistentRead: true,
|
|
58
|
-
});
|
|
59
|
-
// Add nonce
|
|
60
|
-
await cache.add(nonce, 60);
|
|
61
|
-
(0, vitest_1.expect)(mockDynamoDB.putItem).toHaveBeenCalledWith({
|
|
62
|
-
TableName: "test-nonce-table",
|
|
63
|
-
Item: {
|
|
64
|
-
nonce: { S: nonce },
|
|
65
|
-
expiresAt: { N: vitest_1.expect.any(String) },
|
|
66
|
-
createdAt: { N: vitest_1.expect.any(String) },
|
|
67
|
-
},
|
|
68
|
-
ConditionExpression: "attribute_not_exists(nonce)",
|
|
69
|
-
});
|
|
70
|
-
// Mock that it now exists
|
|
71
|
-
mockDynamoDB.getItem.mockReturnValue(createMockGetItemResponse(true));
|
|
72
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
(0, vitest_1.it)("should prevent duplicate nonce addition", async () => {
|
|
75
|
-
const nonce = "duplicate-nonce";
|
|
76
|
-
// Mock DynamoDB conditional check failure
|
|
77
|
-
mockDynamoDB.putItem.mockReturnValue(createMockPutItemResponse(false));
|
|
78
|
-
// Adding duplicate nonce should throw
|
|
79
|
-
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Nonce duplicate-nonce already exists - potential replay attack");
|
|
80
|
-
});
|
|
81
|
-
(0, vitest_1.it)("should handle expired nonces correctly", async () => {
|
|
82
|
-
const nonce = "expired-nonce";
|
|
83
|
-
// Mock expired nonce
|
|
84
|
-
mockDynamoDB.getItem.mockReturnValue(createMockGetItemResponse(true, true));
|
|
85
|
-
// Should return false for expired nonce
|
|
86
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
(0, vitest_1.describe)("Error Handling", () => {
|
|
90
|
-
(0, vitest_1.it)("should handle ResourceNotFoundException", async () => {
|
|
91
|
-
const nonce = "missing-table-nonce";
|
|
92
|
-
const error = new Error("Table not found");
|
|
93
|
-
error.code = "ResourceNotFoundException";
|
|
94
|
-
mockDynamoDB.getItem.mockReturnValue({
|
|
95
|
-
promise: () => Promise.reject(error),
|
|
96
|
-
});
|
|
97
|
-
// Should return false for missing table
|
|
98
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
99
|
-
});
|
|
100
|
-
(0, vitest_1.it)("should handle ValidationException on add", async () => {
|
|
101
|
-
const nonce = "validation-error-nonce";
|
|
102
|
-
const error = new Error("Invalid request");
|
|
103
|
-
error.code = "ValidationException";
|
|
104
|
-
mockDynamoDB.putItem.mockReturnValue({
|
|
105
|
-
promise: () => Promise.reject(error),
|
|
106
|
-
});
|
|
107
|
-
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Invalid DynamoDB operation");
|
|
108
|
-
});
|
|
109
|
-
(0, vitest_1.it)("should handle ProvisionedThroughputExceededException", async () => {
|
|
110
|
-
const nonce = "throughput-error-nonce";
|
|
111
|
-
const error = new Error("Throughput exceeded");
|
|
112
|
-
error.code = "ProvisionedThroughputExceededException";
|
|
113
|
-
mockDynamoDB.putItem.mockReturnValue({
|
|
114
|
-
promise: () => Promise.reject(error),
|
|
115
|
-
});
|
|
116
|
-
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("DynamoDB throughput exceeded");
|
|
117
|
-
});
|
|
118
|
-
(0, vitest_1.it)("should propagate unknown errors with context", async () => {
|
|
119
|
-
const nonce = "unknown-error-nonce";
|
|
120
|
-
const error = new Error("Unknown error");
|
|
121
|
-
error.code = "UnknownException";
|
|
122
|
-
mockDynamoDB.getItem.mockReturnValue({
|
|
123
|
-
promise: () => Promise.reject(error),
|
|
124
|
-
});
|
|
125
|
-
await (0, vitest_1.expect)(cache.has(nonce)).rejects.toThrow("Failed to check nonce existence");
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
(0, vitest_1.describe)("Atomic Operations", () => {
|
|
129
|
-
(0, vitest_1.it)("should use conditional write for atomicity", async () => {
|
|
130
|
-
const nonce = "atomic-test-nonce";
|
|
131
|
-
const ttl = 300;
|
|
132
|
-
mockDynamoDB.putItem.mockReturnValue(createMockPutItemResponse(true));
|
|
133
|
-
await cache.add(nonce, ttl);
|
|
134
|
-
// Verify conditional expression was used
|
|
135
|
-
(0, vitest_1.expect)(mockDynamoDB.putItem).toHaveBeenCalledWith({
|
|
136
|
-
TableName: "test-nonce-table",
|
|
137
|
-
Item: {
|
|
138
|
-
nonce: { S: nonce },
|
|
139
|
-
expiresAt: { N: vitest_1.expect.any(String) },
|
|
140
|
-
createdAt: { N: vitest_1.expect.any(String) },
|
|
141
|
-
},
|
|
142
|
-
ConditionExpression: "attribute_not_exists(nonce)",
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
(0, vitest_1.it)("should use consistent reads for has() operations", async () => {
|
|
146
|
-
const nonce = "consistent-read-nonce";
|
|
147
|
-
mockDynamoDB.getItem.mockReturnValue(createMockGetItemResponse(false));
|
|
148
|
-
await cache.has(nonce);
|
|
149
|
-
(0, vitest_1.expect)(mockDynamoDB.getItem).toHaveBeenCalledWith({
|
|
150
|
-
TableName: "test-nonce-table",
|
|
151
|
-
Key: { nonce: { S: nonce } },
|
|
152
|
-
ConsistentRead: true,
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
(0, vitest_1.describe)("Cleanup", () => {
|
|
157
|
-
(0, vitest_1.it)("should be a no-op since DynamoDB handles TTL", async () => {
|
|
158
|
-
// cleanup() should not call any DynamoDB methods
|
|
159
|
-
await cache.cleanup();
|
|
160
|
-
(0, vitest_1.expect)(mockDynamoDB.getItem).not.toHaveBeenCalled();
|
|
161
|
-
(0, vitest_1.expect)(mockDynamoDB.putItem).not.toHaveBeenCalled();
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
(0, vitest_1.describe)("Custom Configuration", () => {
|
|
165
|
-
(0, vitest_1.it)("should use custom attribute names", () => {
|
|
166
|
-
const customCache = new dynamodb_nonce_cache_js_1.DynamoNonceCache(mockDynamoDB, "custom-table", "customKey", "customTTL");
|
|
167
|
-
mockDynamoDB.getItem.mockReturnValue(createMockGetItemResponse(false));
|
|
168
|
-
customCache.has("test-nonce");
|
|
169
|
-
(0, vitest_1.expect)(mockDynamoDB.getItem).toHaveBeenCalledWith({
|
|
170
|
-
TableName: "custom-table",
|
|
171
|
-
Key: { customKey: { S: "test-nonce" } },
|
|
172
|
-
ConsistentRead: true,
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Tests for Memory Nonce Cache
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const vitest_1 = require("vitest");
|
|
7
|
-
const memory_nonce_cache_js_1 = require("../memory-nonce-cache.js");
|
|
8
|
-
(0, vitest_1.describe)("MemoryNonceCache", () => {
|
|
9
|
-
let cache;
|
|
10
|
-
(0, vitest_1.beforeEach)(() => {
|
|
11
|
-
cache = new memory_nonce_cache_js_1.MemoryNonceCache(100); // Short cleanup interval for testing
|
|
12
|
-
});
|
|
13
|
-
(0, vitest_1.afterEach)(() => {
|
|
14
|
-
cache.destroy();
|
|
15
|
-
});
|
|
16
|
-
(0, vitest_1.describe)("Basic Operations", () => {
|
|
17
|
-
(0, vitest_1.it)("should add and check nonce existence", async () => {
|
|
18
|
-
const nonce = "test-nonce-123";
|
|
19
|
-
// Initially should not exist
|
|
20
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
21
|
-
// Add nonce
|
|
22
|
-
await cache.add(nonce, 60); // 60 seconds TTL
|
|
23
|
-
// Should now exist
|
|
24
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
25
|
-
});
|
|
26
|
-
(0, vitest_1.it)("should prevent duplicate nonce addition", async () => {
|
|
27
|
-
const nonce = "duplicate-nonce";
|
|
28
|
-
// Add nonce first time
|
|
29
|
-
await cache.add(nonce, 60);
|
|
30
|
-
// Adding same nonce should throw
|
|
31
|
-
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Nonce duplicate-nonce already exists");
|
|
32
|
-
});
|
|
33
|
-
(0, vitest_1.it)("should handle TTL expiration", async () => {
|
|
34
|
-
const nonce = "expiring-nonce";
|
|
35
|
-
// Add nonce with very short TTL
|
|
36
|
-
await cache.add(nonce, 0.1); // 0.1 seconds
|
|
37
|
-
// Should exist initially
|
|
38
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
39
|
-
// Wait for expiration
|
|
40
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
41
|
-
// Should be expired
|
|
42
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
(0, vitest_1.describe)("Cleanup", () => {
|
|
46
|
-
(0, vitest_1.it)("should clean up expired entries", async () => {
|
|
47
|
-
const nonce1 = "nonce-1";
|
|
48
|
-
const nonce2 = "nonce-2";
|
|
49
|
-
// Add nonces with different TTLs
|
|
50
|
-
await cache.add(nonce1, 0.1); // Short TTL
|
|
51
|
-
await cache.add(nonce2, 60); // Long TTL
|
|
52
|
-
// Both should exist initially
|
|
53
|
-
(0, vitest_1.expect)(await cache.has(nonce1)).toBe(true);
|
|
54
|
-
(0, vitest_1.expect)(await cache.has(nonce2)).toBe(true);
|
|
55
|
-
// Wait for first nonce to expire
|
|
56
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
57
|
-
// Manual cleanup
|
|
58
|
-
await cache.cleanup();
|
|
59
|
-
// First should be gone, second should remain
|
|
60
|
-
(0, vitest_1.expect)(await cache.has(nonce1)).toBe(false);
|
|
61
|
-
(0, vitest_1.expect)(await cache.has(nonce2)).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
(0, vitest_1.it)("should provide accurate statistics", async () => {
|
|
64
|
-
const nonce1 = "stats-nonce-1";
|
|
65
|
-
const nonce2 = "stats-nonce-2";
|
|
66
|
-
// Add nonces
|
|
67
|
-
await cache.add(nonce1, 60);
|
|
68
|
-
await cache.add(nonce2, 0.1); // This will expire quickly
|
|
69
|
-
// Wait for one to expire
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
71
|
-
const stats = cache.getStats();
|
|
72
|
-
(0, vitest_1.expect)(stats.size).toBeGreaterThanOrEqual(1); // At least one still in cache
|
|
73
|
-
(0, vitest_1.expect)(stats.expired).toBeGreaterThanOrEqual(0); // Some may be expired
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
(0, vitest_1.describe)("Atomic Operations", () => {
|
|
77
|
-
(0, vitest_1.it)("should prevent duplicate nonce addition", async () => {
|
|
78
|
-
const nonce = "atomic-test-nonce";
|
|
79
|
-
// Add nonce first time
|
|
80
|
-
await cache.add(nonce, 60);
|
|
81
|
-
// Second attempt should fail
|
|
82
|
-
await (0, vitest_1.expect)(cache.add(nonce, 60)).rejects.toThrow("Nonce atomic-test-nonce already exists");
|
|
83
|
-
// Nonce should still exist
|
|
84
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
(0, vitest_1.describe)("Memory Management", () => {
|
|
88
|
-
(0, vitest_1.it)("should clear all entries", async () => {
|
|
89
|
-
// Add multiple nonces
|
|
90
|
-
await cache.add("nonce-1", 60);
|
|
91
|
-
await cache.add("nonce-2", 60);
|
|
92
|
-
await cache.add("nonce-3", 60);
|
|
93
|
-
// All should exist
|
|
94
|
-
(0, vitest_1.expect)(await cache.has("nonce-1")).toBe(true);
|
|
95
|
-
(0, vitest_1.expect)(await cache.has("nonce-2")).toBe(true);
|
|
96
|
-
(0, vitest_1.expect)(await cache.has("nonce-3")).toBe(true);
|
|
97
|
-
// Clear cache
|
|
98
|
-
cache.clear();
|
|
99
|
-
// None should exist
|
|
100
|
-
(0, vitest_1.expect)(await cache.has("nonce-1")).toBe(false);
|
|
101
|
-
(0, vitest_1.expect)(await cache.has("nonce-2")).toBe(false);
|
|
102
|
-
(0, vitest_1.expect)(await cache.has("nonce-3")).toBe(false);
|
|
103
|
-
});
|
|
104
|
-
(0, vitest_1.it)("should properly destroy cache and stop cleanup", async () => {
|
|
105
|
-
const cache2 = new memory_nonce_cache_js_1.MemoryNonceCache(50);
|
|
106
|
-
await cache2.add("test-nonce", 60);
|
|
107
|
-
(0, vitest_1.expect)(await cache2.has("test-nonce")).toBe(true);
|
|
108
|
-
// Destroy should clear cache and stop intervals
|
|
109
|
-
cache2.destroy();
|
|
110
|
-
(0, vitest_1.expect)(await cache2.has("test-nonce")).toBe(false);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
(0, vitest_1.describe)("Edge Cases", () => {
|
|
114
|
-
(0, vitest_1.it)("should handle empty nonce strings", async () => {
|
|
115
|
-
await (0, vitest_1.expect)(cache.add("", 60)).resolves.not.toThrow();
|
|
116
|
-
(0, vitest_1.expect)(await cache.has("")).toBe(true);
|
|
117
|
-
});
|
|
118
|
-
(0, vitest_1.it)("should handle zero TTL", async () => {
|
|
119
|
-
const nonce = "zero-ttl-nonce";
|
|
120
|
-
await cache.add(nonce, 0);
|
|
121
|
-
// Zero TTL means it should be expired immediately
|
|
122
|
-
// The implementation stores it but it should be considered expired
|
|
123
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
124
|
-
});
|
|
125
|
-
(0, vitest_1.it)("should handle negative TTL", async () => {
|
|
126
|
-
const nonce = "negative-ttl-nonce";
|
|
127
|
-
await cache.add(nonce, -1);
|
|
128
|
-
// Should be immediately expired
|
|
129
|
-
(0, vitest_1.expect)(await cache.has(nonce)).toBe(false);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
});
|