@longarc/mdash 3.1.2 → 3.1.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.
Files changed (172) hide show
  1. package/README.md +86 -23
  2. package/SECURITY.md +254 -0
  3. package/dist/accountability/engine.d.ts +27 -0
  4. package/dist/accountability/engine.d.ts.map +1 -0
  5. package/dist/accountability/engine.js +148 -0
  6. package/dist/accountability/engine.js.map +1 -0
  7. package/dist/accountability/types.d.ts +46 -0
  8. package/dist/accountability/types.d.ts.map +1 -0
  9. package/dist/accountability/types.js +8 -0
  10. package/dist/accountability/types.js.map +1 -0
  11. package/dist/checkpoint/engine.d.ts.map +1 -1
  12. package/dist/checkpoint/engine.js +4 -0
  13. package/dist/checkpoint/engine.js.map +1 -1
  14. package/dist/context/compose.d.ts +62 -0
  15. package/dist/context/compose.d.ts.map +1 -0
  16. package/dist/context/compose.js +286 -0
  17. package/dist/context/compose.js.map +1 -0
  18. package/dist/context/crypto/hash.d.ts +100 -0
  19. package/dist/context/crypto/hash.d.ts.map +1 -0
  20. package/dist/context/crypto/hash.js +248 -0
  21. package/dist/context/crypto/hash.js.map +1 -0
  22. package/dist/context/crypto/hmac.d.ts +80 -0
  23. package/dist/context/crypto/hmac.d.ts.map +1 -0
  24. package/dist/context/crypto/hmac.js +192 -0
  25. package/dist/context/crypto/hmac.js.map +1 -0
  26. package/dist/context/crypto/index.d.ts +7 -0
  27. package/dist/context/crypto/index.d.ts.map +1 -0
  28. package/dist/context/crypto/index.js +7 -0
  29. package/dist/context/crypto/index.js.map +1 -0
  30. package/dist/context/engine-v3.0-backup.d.ts +197 -0
  31. package/dist/context/engine-v3.0-backup.d.ts.map +1 -0
  32. package/dist/context/engine-v3.0-backup.js +392 -0
  33. package/dist/context/engine-v3.0-backup.js.map +1 -0
  34. package/dist/context/fragment.d.ts +99 -0
  35. package/dist/context/fragment.d.ts.map +1 -0
  36. package/dist/context/fragment.js +316 -0
  37. package/dist/context/fragment.js.map +1 -0
  38. package/dist/context/index.d.ts +99 -0
  39. package/dist/context/index.d.ts.map +1 -0
  40. package/dist/context/index.js +180 -0
  41. package/dist/context/index.js.map +1 -0
  42. package/dist/context/provenance.d.ts +80 -0
  43. package/dist/context/provenance.d.ts.map +1 -0
  44. package/dist/context/provenance.js +294 -0
  45. package/dist/context/provenance.js.map +1 -0
  46. package/dist/context/resolve.d.ts +106 -0
  47. package/dist/context/resolve.d.ts.map +1 -0
  48. package/dist/context/resolve.js +440 -0
  49. package/dist/context/resolve.js.map +1 -0
  50. package/dist/context/store.d.ts +156 -0
  51. package/dist/context/store.d.ts.map +1 -0
  52. package/dist/context/store.js +396 -0
  53. package/dist/context/store.js.map +1 -0
  54. package/dist/context/types.d.ts +463 -0
  55. package/dist/context/types.d.ts.map +1 -0
  56. package/dist/context/types.js +94 -0
  57. package/dist/context/types.js.map +1 -0
  58. package/dist/context/utils/atomic.d.ts +76 -0
  59. package/dist/context/utils/atomic.d.ts.map +1 -0
  60. package/dist/context/utils/atomic.js +159 -0
  61. package/dist/context/utils/atomic.js.map +1 -0
  62. package/dist/context/utils/credit.d.ts +65 -0
  63. package/dist/context/utils/credit.d.ts.map +1 -0
  64. package/dist/context/utils/credit.js +164 -0
  65. package/dist/context/utils/credit.js.map +1 -0
  66. package/dist/context/utils/index.d.ts +13 -0
  67. package/dist/context/utils/index.d.ts.map +1 -0
  68. package/dist/context/utils/index.js +13 -0
  69. package/dist/context/utils/index.js.map +1 -0
  70. package/dist/context/utils/utility.d.ts +63 -0
  71. package/dist/context/utils/utility.d.ts.map +1 -0
  72. package/dist/context/utils/utility.js +141 -0
  73. package/dist/context/utils/utility.js.map +1 -0
  74. package/dist/core/commitment.d.ts +25 -2
  75. package/dist/core/commitment.d.ts.map +1 -1
  76. package/dist/core/commitment.js +44 -6
  77. package/dist/core/commitment.js.map +1 -1
  78. package/dist/core/crypto.d.ts +2 -0
  79. package/dist/core/crypto.d.ts.map +1 -1
  80. package/dist/core/crypto.js +12 -0
  81. package/dist/core/crypto.js.map +1 -1
  82. package/dist/index.d.ts +11 -6
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +35 -10
  85. package/dist/index.js.map +1 -1
  86. package/dist/mcca/engine.d.ts.map +1 -1
  87. package/dist/mcca/engine.js +5 -4
  88. package/dist/mcca/engine.js.map +1 -1
  89. package/dist/physics/engine.d.ts +1 -0
  90. package/dist/physics/engine.d.ts.map +1 -1
  91. package/dist/physics/engine.js +36 -2
  92. package/dist/physics/engine.js.map +1 -1
  93. package/dist/provenance/api-handler.d.ts +45 -0
  94. package/dist/provenance/api-handler.d.ts.map +1 -0
  95. package/dist/provenance/api-handler.js +223 -0
  96. package/dist/provenance/api-handler.js.map +1 -0
  97. package/dist/provenance/api-types.d.ts +108 -0
  98. package/dist/provenance/api-types.d.ts.map +1 -0
  99. package/dist/provenance/api-types.js +9 -0
  100. package/dist/provenance/api-types.js.map +1 -0
  101. package/dist/provenance/index.d.ts +6 -0
  102. package/dist/provenance/index.d.ts.map +1 -0
  103. package/dist/provenance/index.js +3 -0
  104. package/dist/provenance/index.js.map +1 -0
  105. package/dist/provenance/provenance-engine.d.ts +63 -0
  106. package/dist/provenance/provenance-engine.d.ts.map +1 -0
  107. package/dist/provenance/provenance-engine.js +311 -0
  108. package/dist/provenance/provenance-engine.js.map +1 -0
  109. package/dist/provenance/types.d.ts +193 -0
  110. package/dist/provenance/types.d.ts.map +1 -0
  111. package/dist/provenance/types.js +9 -0
  112. package/dist/provenance/types.js.map +1 -0
  113. package/dist/tee/engine.d.ts.map +1 -1
  114. package/dist/tee/engine.js +14 -0
  115. package/dist/tee/engine.js.map +1 -1
  116. package/dist/warrant/engine.d.ts +24 -1
  117. package/dist/warrant/engine.d.ts.map +1 -1
  118. package/dist/warrant/engine.js +76 -1
  119. package/dist/warrant/engine.js.map +1 -1
  120. package/dist/zk/engine.d.ts.map +1 -1
  121. package/dist/zk/engine.js +7 -4
  122. package/dist/zk/engine.js.map +1 -1
  123. package/docs/SECURITY-PATCHES.md +170 -0
  124. package/package.json +17 -5
  125. package/src/__tests__/accountability.test.ts +308 -0
  126. package/src/__tests__/l1-verification-modes.test.ts +424 -0
  127. package/src/__tests__/phase1.benchmark.test.ts +94 -0
  128. package/src/__tests__/phase1.test.ts +0 -77
  129. package/src/__tests__/phase2-4.benchmark.test.ts +60 -0
  130. package/src/__tests__/phase2-4.test.ts +1 -52
  131. package/src/__tests__/provenance/api-handler.test.ts +356 -0
  132. package/src/__tests__/provenance/provenance-engine.test.ts +628 -0
  133. package/src/__tests__/sa-2026-008.test.ts +45 -0
  134. package/src/__tests__/sa-2026-009.test.ts +86 -0
  135. package/src/__tests__/sa-2026-010.test.ts +72 -0
  136. package/src/__tests__/sa-2026-012.test.ts +65 -0
  137. package/src/__tests__/sa-2026-nfc.test.ts +40 -0
  138. package/src/__tests__/security.test.ts +786 -0
  139. package/src/accountability/engine.ts +230 -0
  140. package/src/accountability/types.ts +58 -0
  141. package/src/checkpoint/engine.ts +4 -0
  142. package/src/context/__tests__/caret-v0.2.0.test.ts +860 -0
  143. package/src/context/__tests__/integration.test.ts +356 -0
  144. package/src/context/compose.ts +388 -0
  145. package/src/context/crypto/hash.ts +277 -0
  146. package/src/context/crypto/hmac.ts +253 -0
  147. package/src/context/crypto/index.ts +29 -0
  148. package/src/context/engine-v3.0-backup.ts +598 -0
  149. package/src/context/fragment.ts +454 -0
  150. package/src/context/index.ts +427 -0
  151. package/src/context/provenance.ts +380 -0
  152. package/src/context/resolve.ts +581 -0
  153. package/src/context/store.ts +503 -0
  154. package/src/context/types.ts +679 -0
  155. package/src/context/utils/atomic.ts +207 -0
  156. package/src/context/utils/credit.ts +224 -0
  157. package/src/context/utils/index.ts +13 -0
  158. package/src/context/utils/utility.ts +200 -0
  159. package/src/core/commitment.ts +129 -67
  160. package/src/core/crypto.ts +13 -0
  161. package/src/index.ts +62 -10
  162. package/src/mcca/engine.ts +5 -4
  163. package/src/physics/engine.ts +40 -3
  164. package/src/provenance/api-handler.ts +248 -0
  165. package/src/provenance/api-types.ts +112 -0
  166. package/src/provenance/index.ts +19 -0
  167. package/src/provenance/provenance-engine.ts +387 -0
  168. package/src/provenance/types.ts +211 -0
  169. package/src/tee/engine.ts +16 -0
  170. package/src/warrant/engine.ts +89 -1
  171. package/src/zk/engine.ts +8 -4
  172. package/tsconfig.json +1 -1
@@ -0,0 +1,786 @@
1
+ /**
2
+ * mdash v3.0 - Security Test Suite
3
+ *
4
+ * Adversarial testing for:
5
+ * - Adversarial input fuzzing
6
+ * - Concurrent access (TOCTOU verification)
7
+ * - Replay attack resistance
8
+ * - Cross-layer failure handling
9
+ *
10
+ * @version 3.0.0
11
+ */
12
+
13
+ import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'vitest';
14
+
15
+ import {
16
+ // Crypto
17
+ sha256,
18
+ sha256Object,
19
+ hmacSeal,
20
+ deriveKey,
21
+ generateTimestamp,
22
+ generateWarrantId,
23
+ sanitizeObject,
24
+ Hash,
25
+ Timestamp,
26
+ WarrantId,
27
+
28
+ // Engines
29
+ CommitmentEngine,
30
+ WarrantEngine,
31
+ PhysicsEngine,
32
+ CheckpointEngine,
33
+
34
+ // TEE
35
+ TEEAttestationEngine,
36
+ AttestationBridge,
37
+
38
+ // Protocol
39
+ MdashProtocol,
40
+ createMdash,
41
+ } from '../index';
42
+
43
+ const TEST_SEAL_KEY = 'test-seal-key-security-suite-minimum-32-chars';
44
+
45
+ // ============================================================================
46
+ // ADVERSARIAL INPUT TESTING
47
+ // ============================================================================
48
+
49
+ describe('Adversarial Input Testing', () => {
50
+ let protocol: MdashProtocol;
51
+
52
+ beforeAll(async () => {
53
+ protocol = createMdash({ sealKey: TEST_SEAL_KEY });
54
+ await protocol.initialize();
55
+ });
56
+
57
+ describe('Prototype Pollution Resistance', () => {
58
+ it('should sanitize __proto__ in warrant constraints', async () => {
59
+ const maliciousConstraints = {
60
+ maxAmount: 1000,
61
+ __proto__: { isAdmin: true },
62
+ };
63
+
64
+ // Sanitize before use
65
+ const sanitized = sanitizeObject(maliciousConstraints);
66
+ expect(Object.keys(sanitized)).not.toContain('__proto__');
67
+ expect((sanitized as any).maxAmount).toBe(1000);
68
+ });
69
+
70
+ it('should sanitize constructor pollution attempts', async () => {
71
+ const malicious = {
72
+ maxAmount: 1000,
73
+ constructor: { prototype: { polluted: true } },
74
+ };
75
+
76
+ const sanitized = sanitizeObject(malicious);
77
+ expect(Object.keys(sanitized)).not.toContain('constructor');
78
+ });
79
+
80
+ it('should handle nested prototype pollution', async () => {
81
+ const malicious = {
82
+ nested: {
83
+ data: 'value',
84
+ __proto__: { polluted: true },
85
+ },
86
+ };
87
+
88
+ const sanitized = sanitizeObject(malicious);
89
+ expect(Object.keys((sanitized as any).nested)).not.toContain('__proto__');
90
+ });
91
+
92
+ it('should sanitize action params before physics validation', async () => {
93
+ const warrant = await protocol.prestageWarrant({
94
+ agentId: 'proto-test-agent',
95
+ policyId: 'financial-transfer-v2',
96
+ tier: 'T2',
97
+ constraints: { maxAmount: 10000 },
98
+ durationMs: 60000,
99
+ issuedBy: 'test@example.com',
100
+ });
101
+
102
+ // Attempt to inject malicious params
103
+ const maliciousParams = {
104
+ amount: 500,
105
+ __proto__: { maxAmount: Infinity },
106
+ };
107
+
108
+ // Should not throw, should sanitize internally
109
+ const result = await protocol.execute({
110
+ agentId: 'proto-test-agent',
111
+ action: 'transfer',
112
+ actionParams: sanitizeObject(maliciousParams),
113
+ execute: async () => ({ success: true }),
114
+ });
115
+
116
+ expect(result.validation.valid).toBe(true);
117
+ });
118
+ });
119
+
120
+ describe('Boundary Value Fuzzing', () => {
121
+ it('should handle maxAmount at Number.MAX_SAFE_INTEGER', async () => {
122
+ const warrant = await protocol.prestageWarrant({
123
+ agentId: 'boundary-agent-1',
124
+ policyId: 'financial-transfer-v2',
125
+ tier: 'T3',
126
+ constraints: { maxAmount: Number.MAX_SAFE_INTEGER },
127
+ durationMs: 60000,
128
+ issuedBy: 'test@example.com',
129
+ });
130
+
131
+ expect(warrant.constraints.maxAmount).toBe(Number.MAX_SAFE_INTEGER);
132
+ });
133
+
134
+ it('should reject negative amounts (SA-2026-011)', async () => {
135
+ await protocol.prestageWarrant({
136
+ agentId: 'negative-amount-agent',
137
+ policyId: 'financial-transfer-v2',
138
+ tier: 'T2',
139
+ constraints: { maxAmount: 1000 },
140
+ durationMs: 60000,
141
+ issuedBy: 'test@example.com',
142
+ });
143
+
144
+ // SA-2026-011: Negative amounts are now explicitly rejected
145
+ await expect(
146
+ protocol.execute({
147
+ agentId: 'negative-amount-agent',
148
+ action: 'transfer',
149
+ actionParams: { amount: -100 },
150
+ execute: async () => ({ success: true }),
151
+ })
152
+ ).rejects.toThrow('Authorization denied');
153
+ });
154
+
155
+ it('should reject NaN in amount (SA-2026-011)', async () => {
156
+ await protocol.prestageWarrant({
157
+ agentId: 'nan-agent',
158
+ policyId: 'financial-transfer-v2',
159
+ tier: 'T2',
160
+ constraints: { maxAmount: 1000 },
161
+ durationMs: 60000,
162
+ issuedBy: 'test@example.com',
163
+ });
164
+
165
+ // SA-2026-011: NaN amounts are now explicitly rejected
166
+ await expect(
167
+ protocol.execute({
168
+ agentId: 'nan-agent',
169
+ action: 'transfer',
170
+ actionParams: { amount: NaN },
171
+ execute: async () => ({ success: true }),
172
+ })
173
+ ).rejects.toThrow('Authorization denied');
174
+ });
175
+
176
+ it('should reject -Infinity in amount (SA-2026-011)', async () => {
177
+ await protocol.prestageWarrant({
178
+ agentId: 'neg-infinity-agent',
179
+ policyId: 'financial-transfer-v2',
180
+ tier: 'T2',
181
+ constraints: { maxAmount: 1000 },
182
+ durationMs: 60000,
183
+ issuedBy: 'test@example.com',
184
+ });
185
+
186
+ // SA-2026-011: -Infinity < 0, caught by negative check
187
+ await expect(
188
+ protocol.execute({
189
+ agentId: 'neg-infinity-agent',
190
+ action: 'transfer',
191
+ actionParams: { amount: -Infinity },
192
+ execute: async () => ({ success: true }),
193
+ })
194
+ ).rejects.toThrow('Authorization denied');
195
+ });
196
+
197
+ it('should still allow valid amounts within limits (SA-2026-011 regression)', async () => {
198
+ await protocol.prestageWarrant({
199
+ agentId: 'valid-amount-agent',
200
+ policyId: 'financial-transfer-v2',
201
+ tier: 'T2',
202
+ constraints: { maxAmount: 1000 },
203
+ durationMs: 60000,
204
+ issuedBy: 'test@example.com',
205
+ });
206
+
207
+ const result = await protocol.execute({
208
+ agentId: 'valid-amount-agent',
209
+ action: 'transfer',
210
+ actionParams: { amount: 50 },
211
+ execute: async () => ({ success: true }),
212
+ });
213
+
214
+ expect(result.validation.valid).toBe(true);
215
+ });
216
+
217
+ it('should block Infinity in amount', async () => {
218
+ await protocol.prestageWarrant({
219
+ agentId: 'infinity-amount-agent',
220
+ policyId: 'financial-transfer-v2',
221
+ tier: 'T2',
222
+ constraints: { maxAmount: 1000 },
223
+ durationMs: 60000,
224
+ issuedBy: 'test@example.com',
225
+ });
226
+
227
+ // Infinity > 1000 is true, so should fail physics validation
228
+ await expect(
229
+ protocol.execute({
230
+ agentId: 'infinity-amount-agent',
231
+ action: 'transfer',
232
+ actionParams: { amount: Infinity },
233
+ execute: async () => ({ success: true }),
234
+ })
235
+ ).rejects.toThrow('Authorization denied');
236
+ });
237
+ });
238
+
239
+ describe('Timestamp Manipulation', () => {
240
+ it('should reject warrants with past expiry', async () => {
241
+ // Create warrant with very short duration
242
+ const warrant = await protocol.prestageWarrant({
243
+ agentId: 'timestamp-agent',
244
+ policyId: 'financial-transfer-v2',
245
+ tier: 'T2',
246
+ constraints: { maxAmount: 1000 },
247
+ durationMs: 50, // 50ms - will expire quickly after activation
248
+ issuedBy: 'test@example.com',
249
+ });
250
+
251
+ // Activate immediately
252
+ await protocol.warrant.activate(warrant.id);
253
+
254
+ // Wait for expiry
255
+ await new Promise(resolve => setTimeout(resolve, 150));
256
+
257
+ // Execute should fail - warrant expired
258
+ // Note: Current implementation may need warrant expiry check enhancement
259
+ try {
260
+ await protocol.execute({
261
+ agentId: 'timestamp-agent',
262
+ action: 'transfer',
263
+ actionParams: { amount: 100 },
264
+ execute: async () => ({ success: true }),
265
+ });
266
+ // If we get here without throwing, the expiry check isn't working
267
+ // This documents current behavior that may need hardening
268
+ } catch (e: any) {
269
+ expect(e.message).toBe('Authorization denied');
270
+ }
271
+ });
272
+
273
+ it('should handle timestamp at Unix epoch', async () => {
274
+ const epochTimestamp = new Date(0).toISOString() as Timestamp;
275
+
276
+ // Hashing should work regardless of timestamp value
277
+ const hash = await sha256Object({ timestamp: epochTimestamp });
278
+ expect(hash).toBeTruthy();
279
+ });
280
+
281
+ it('should handle timestamp far in future', async () => {
282
+ const futureTimestamp = new Date('2100-01-01T00:00:00Z').toISOString() as Timestamp;
283
+
284
+ const hash = await sha256Object({ timestamp: futureTimestamp });
285
+ expect(hash).toBeTruthy();
286
+ });
287
+ });
288
+
289
+ describe('String Injection Attempts', () => {
290
+ it('should handle SQL-like injection in agent_id', async () => {
291
+ const maliciousAgentId = "agent'; DROP TABLE warrants; --";
292
+
293
+ const warrant = await protocol.prestageWarrant({
294
+ agentId: maliciousAgentId,
295
+ policyId: 'financial-transfer-v2',
296
+ tier: 'T2',
297
+ constraints: { maxAmount: 1000 },
298
+ durationMs: 60000,
299
+ issuedBy: 'test@example.com',
300
+ });
301
+
302
+ // Should store literally, not interpret
303
+ expect(warrant.agent_id).toBe(maliciousAgentId);
304
+ });
305
+
306
+ it('should handle null bytes in strings', async () => {
307
+ const nullByteAgentId = "agent\x00injected";
308
+
309
+ const warrant = await protocol.prestageWarrant({
310
+ agentId: nullByteAgentId,
311
+ policyId: 'financial-transfer-v2',
312
+ tier: 'T2',
313
+ constraints: { maxAmount: 1000 },
314
+ durationMs: 60000,
315
+ issuedBy: 'test@example.com',
316
+ });
317
+
318
+ expect(warrant.agent_id).toBe(nullByteAgentId);
319
+ });
320
+
321
+ it('should handle unicode normalization attacks', async () => {
322
+ // Two visually identical strings with different unicode
323
+ const agentId1 = "café"; // Normal
324
+ const agentId2 = "café"; // Using combining character (if different)
325
+
326
+ const hash1 = await sha256(agentId1);
327
+ const hash2 = await sha256(agentId2);
328
+
329
+ // Document current behavior - may or may not be equal depending on normalization
330
+ // This test documents the behavior for awareness
331
+ expect(typeof hash1).toBe('string');
332
+ expect(typeof hash2).toBe('string');
333
+ });
334
+ });
335
+ });
336
+
337
+ // ============================================================================
338
+ // CONCURRENT ACCESS TESTING (TOCTOU Verification)
339
+ // ============================================================================
340
+
341
+ describe('Concurrent Access Testing', () => {
342
+ let protocol: MdashProtocol;
343
+
344
+ beforeEach(async () => {
345
+ protocol = createMdash({ sealKey: TEST_SEAL_KEY });
346
+ await protocol.initialize();
347
+ });
348
+
349
+ it('should handle concurrent warrant activations', async () => {
350
+ // Create multiple speculative warrants
351
+ const warrants = await Promise.all([
352
+ protocol.prestageWarrant({
353
+ agentId: 'concurrent-agent',
354
+ policyId: 'financial-transfer-v2',
355
+ tier: 'T2',
356
+ constraints: { maxAmount: 1000 },
357
+ durationMs: 60000,
358
+ issuedBy: 'test@example.com',
359
+ }),
360
+ protocol.prestageWarrant({
361
+ agentId: 'concurrent-agent',
362
+ policyId: 'financial-transfer-v2',
363
+ tier: 'T2',
364
+ constraints: { maxAmount: 2000 },
365
+ durationMs: 60000,
366
+ issuedBy: 'test@example.com',
367
+ }),
368
+ ]);
369
+
370
+ // Both should exist
371
+ expect(warrants.length).toBe(2);
372
+ expect(warrants[0].id).not.toBe(warrants[1].id);
373
+ });
374
+
375
+ it('should handle revocation during execute flow', async () => {
376
+ await protocol.prestageWarrant({
377
+ agentId: 'revoke-race-agent',
378
+ policyId: 'financial-transfer-v2',
379
+ tier: 'T2',
380
+ constraints: { maxAmount: 1000 },
381
+ durationMs: 60000,
382
+ issuedBy: 'test@example.com',
383
+ });
384
+
385
+ // Start execute but simulate revocation during execution
386
+ let executeCalled = false;
387
+
388
+ const executePromise = protocol.execute({
389
+ agentId: 'revoke-race-agent',
390
+ action: 'transfer',
391
+ actionParams: { amount: 100 },
392
+ execute: async () => {
393
+ executeCalled = true;
394
+ // Simulate slow operation
395
+ await new Promise(resolve => setTimeout(resolve, 10));
396
+ return { success: true };
397
+ },
398
+ });
399
+
400
+ // Execute should complete (revocation happens after TOCTOU check in current impl)
401
+ const result = await executePromise;
402
+ expect(executeCalled).toBe(true);
403
+ expect(result.result.success).toBe(true);
404
+ });
405
+
406
+ it('should serialize access under high concurrency', async () => {
407
+ // Pre-stage multiple warrants
408
+ for (let i = 0; i < 10; i++) {
409
+ await protocol.prestageWarrant({
410
+ agentId: `high-concurrency-agent-${i}`,
411
+ policyId: 'financial-transfer-v2',
412
+ tier: 'T2',
413
+ constraints: { maxAmount: 1000 },
414
+ durationMs: 60000,
415
+ issuedBy: 'test@example.com',
416
+ });
417
+ }
418
+
419
+ // Execute all concurrently
420
+ const results = await Promise.all(
421
+ Array.from({ length: 10 }, (_, i) =>
422
+ protocol.execute({
423
+ agentId: `high-concurrency-agent-${i}`,
424
+ action: 'transfer',
425
+ actionParams: { amount: 100 },
426
+ execute: async () => ({ success: true, index: i }),
427
+ })
428
+ )
429
+ );
430
+
431
+ // All should succeed
432
+ expect(results.length).toBe(10);
433
+ results.forEach((r, i) => {
434
+ expect(r.result.success).toBe(true);
435
+ expect(r.result.index).toBe(i);
436
+ });
437
+ });
438
+
439
+ it('should handle warrant rate limiting under burst', async () => {
440
+ const createPromises: Promise<any>[] = [];
441
+
442
+ // Attempt to create 150 warrants rapidly (limit is 100/min)
443
+ for (let i = 0; i < 150; i++) {
444
+ createPromises.push(
445
+ protocol.prestageWarrant({
446
+ agentId: `burst-agent-${i}`,
447
+ policyId: 'financial-transfer-v2',
448
+ tier: 'T2',
449
+ constraints: { maxAmount: 1000 },
450
+ durationMs: 60000,
451
+ issuedBy: 'burst-issuer@example.com', // Same issuer
452
+ }).catch(e => e)
453
+ );
454
+ }
455
+
456
+ const results = await Promise.all(createPromises);
457
+
458
+ // Some should succeed, some should fail with rate limit
459
+ const successes = results.filter(r => r.id !== undefined);
460
+ const failures = results.filter(r => r instanceof Error);
461
+
462
+ expect(successes.length).toBe(100); // Rate limit
463
+ expect(failures.length).toBe(50);
464
+ expect(failures[0].message).toContain('rate limit');
465
+ });
466
+ });
467
+
468
+ // ============================================================================
469
+ // REPLAY ATTACK TESTING
470
+ // ============================================================================
471
+
472
+ describe('Replay Attack Resistance', () => {
473
+ let protocol: MdashProtocol;
474
+ let commitmentEngine: CommitmentEngine;
475
+ let teeEngine: TEEAttestationEngine;
476
+
477
+ beforeAll(async () => {
478
+ protocol = createMdash({ sealKey: TEST_SEAL_KEY });
479
+ await protocol.initialize();
480
+
481
+ commitmentEngine = new CommitmentEngine();
482
+ await commitmentEngine.initialize(TEST_SEAL_KEY);
483
+
484
+ teeEngine = new TEEAttestationEngine(commitmentEngine);
485
+ await teeEngine.initialize(TEST_SEAL_KEY);
486
+ });
487
+
488
+ it('should detect expired attestation documents', async () => {
489
+ const attestation = await teeEngine.attest(
490
+ { action: 'test', timestamp: Date.now() },
491
+ 'replay-test-001'
492
+ );
493
+
494
+ // Manually expire
495
+ (attestation as any).expires_at = new Date(Date.now() - 1000).toISOString();
496
+
497
+ const result = await teeEngine.verify(attestation);
498
+
499
+ expect(result.valid).toBe(false);
500
+ expect(result.errors).toContain('Attestation document expired');
501
+ });
502
+
503
+ it('should detect tampered attestation seal', async () => {
504
+ const attestation = await teeEngine.attest(
505
+ { action: 'test', timestamp: Date.now() },
506
+ 'tamper-test-001'
507
+ );
508
+
509
+ // Tamper with the attested data hash
510
+ (attestation as any).attested_data = 'tampered-hash-value';
511
+
512
+ const result = await teeEngine.verify(attestation);
513
+
514
+ expect(result.valid).toBe(false);
515
+ expect(result.errors).toContain('Invalid document seal');
516
+ });
517
+
518
+ it('should reject attestation with missing L1 commitment', async () => {
519
+ const attestation = await teeEngine.attest(
520
+ { action: 'test', timestamp: Date.now() },
521
+ 'orphan-attestation-001'
522
+ );
523
+
524
+ // Create a new TEE engine without the commitment
525
+ const freshCommitmentEngine = new CommitmentEngine();
526
+ await freshCommitmentEngine.initialize(TEST_SEAL_KEY);
527
+
528
+ const freshTeeEngine = new TEEAttestationEngine(freshCommitmentEngine);
529
+ await freshTeeEngine.initialize(TEST_SEAL_KEY);
530
+
531
+ // Verify with fresh engine that doesn't have the L1 commitment
532
+ const result = await freshTeeEngine.verify(attestation);
533
+
534
+ expect(result.valid).toBe(false);
535
+ expect(result.errors).toContain('L1 commitment not found');
536
+ });
537
+
538
+ it('should generate unique IDs for each attestation', async () => {
539
+ const attestations = await Promise.all([
540
+ teeEngine.attest({ action: 'test1' }, 'unique-test-001'),
541
+ teeEngine.attest({ action: 'test2' }, 'unique-test-002'),
542
+ teeEngine.attest({ action: 'test3' }, 'unique-test-003'),
543
+ ]);
544
+
545
+ const ids = attestations.map(a => a.id);
546
+ const uniqueIds = new Set(ids);
547
+
548
+ expect(uniqueIds.size).toBe(3);
549
+ });
550
+
551
+ it('should include timestamp in attestation to prevent replay', async () => {
552
+ const before = Date.now();
553
+
554
+ const attestation = await teeEngine.attest(
555
+ { action: 'timestamp-test' },
556
+ 'timestamp-test-001'
557
+ );
558
+
559
+ const after = Date.now();
560
+ const attestationTime = new Date(attestation.timestamp).getTime();
561
+
562
+ expect(attestationTime).toBeGreaterThanOrEqual(before);
563
+ expect(attestationTime).toBeLessThanOrEqual(after);
564
+ });
565
+ });
566
+
567
+ // ============================================================================
568
+ // CROSS-LAYER FAILURE TESTING
569
+ // ============================================================================
570
+
571
+ describe('Cross-Layer Failure Handling', () => {
572
+ let protocol: MdashProtocol;
573
+ let commitmentEngine: CommitmentEngine;
574
+ let teeEngine: TEEAttestationEngine;
575
+ let bridge: AttestationBridge;
576
+
577
+ beforeAll(async () => {
578
+ protocol = createMdash({ sealKey: TEST_SEAL_KEY });
579
+ await protocol.initialize();
580
+
581
+ commitmentEngine = new CommitmentEngine();
582
+ await commitmentEngine.initialize(TEST_SEAL_KEY);
583
+
584
+ teeEngine = new TEEAttestationEngine(commitmentEngine);
585
+ await teeEngine.initialize(TEST_SEAL_KEY);
586
+
587
+ bridge = new AttestationBridge(teeEngine, commitmentEngine);
588
+ });
589
+
590
+ it('should detect cross-layer commitment reference', async () => {
591
+ // Create attestation through normal flow
592
+ const { commitment, attestation } = await bridge.commitAndAttest(
593
+ { action: 'cross-layer-test' },
594
+ 'cross-layer-001'
595
+ );
596
+
597
+ // Verify with same engine - should pass
598
+ const result = await bridge.verifyBoth(commitment, attestation);
599
+
600
+ expect(result.l1Valid).toBe(true);
601
+ expect(result.l2Valid).toBe(true);
602
+ expect(result.crossLayerValid).toBe(true);
603
+ });
604
+
605
+ it('should detect cross-layer reference mismatch', async () => {
606
+ // Create two separate commit-and-attest operations
607
+ const { commitment: commitment1 } = await bridge.commitAndAttest(
608
+ { action: 'action-1' },
609
+ 'mismatch-test-001'
610
+ );
611
+
612
+ const { attestation: attestation2 } = await bridge.commitAndAttest(
613
+ { action: 'action-2' },
614
+ 'mismatch-test-002'
615
+ );
616
+
617
+ // Try to verify mismatched pair
618
+ const result = await bridge.verifyBoth(commitment1, attestation2);
619
+
620
+ expect(result.crossLayerValid).toBe(false);
621
+ expect(result.errors).toContain('Cross-layer reference mismatch');
622
+ });
623
+
624
+ it('should propagate physics validation failure to checkpoint', async () => {
625
+ await protocol.prestageWarrant({
626
+ agentId: 'physics-fail-agent',
627
+ policyId: 'financial-transfer-v2',
628
+ tier: 'T2',
629
+ constraints: { maxAmount: 100 },
630
+ durationMs: 60000,
631
+ issuedBy: 'test@example.com',
632
+ });
633
+
634
+ // Attempt action that exceeds constraints
635
+ await expect(
636
+ protocol.execute({
637
+ agentId: 'physics-fail-agent',
638
+ action: 'transfer',
639
+ actionParams: { amount: 1000 }, // Exceeds maxAmount
640
+ execute: async () => ({ success: true }),
641
+ })
642
+ ).rejects.toThrow('Authorization denied');
643
+
644
+ // Check that error was logged (would need checkpoint inspection in real impl)
645
+ });
646
+
647
+ it('should handle execute function failure gracefully', async () => {
648
+ await protocol.prestageWarrant({
649
+ agentId: 'execute-fail-agent',
650
+ policyId: 'financial-transfer-v2',
651
+ tier: 'T2',
652
+ constraints: { maxAmount: 10000 },
653
+ durationMs: 60000,
654
+ issuedBy: 'test@example.com',
655
+ });
656
+
657
+ const executeError = new Error('External service unavailable');
658
+
659
+ await expect(
660
+ protocol.execute({
661
+ agentId: 'execute-fail-agent',
662
+ action: 'transfer',
663
+ actionParams: { amount: 100 },
664
+ execute: async () => {
665
+ throw executeError;
666
+ },
667
+ })
668
+ ).rejects.toThrow('External service unavailable');
669
+ });
670
+
671
+ it('should maintain audit trail even on failure', async () => {
672
+ await protocol.prestageWarrant({
673
+ agentId: 'audit-trail-agent',
674
+ policyId: 'financial-transfer-v2',
675
+ tier: 'T2',
676
+ constraints: { maxAmount: 100 },
677
+ durationMs: 60000,
678
+ issuedBy: 'test@example.com',
679
+ });
680
+
681
+ const checkpointStatsBefore = protocol.checkpoint.getStats();
682
+
683
+ try {
684
+ await protocol.execute({
685
+ agentId: 'audit-trail-agent',
686
+ action: 'transfer',
687
+ actionParams: { amount: 1000 }, // Will fail physics
688
+ execute: async () => ({ success: true }),
689
+ });
690
+ } catch {
691
+ // Expected to fail
692
+ }
693
+
694
+ const checkpointStatsAfter = protocol.checkpoint.getStats();
695
+
696
+ // Total checkpoint count should have increased (error checkpoint created)
697
+ expect(checkpointStatsAfter.total).toBeGreaterThanOrEqual(
698
+ checkpointStatsBefore.total
699
+ );
700
+ });
701
+ });
702
+
703
+ // ============================================================================
704
+ // ADDITIONAL SECURITY EDGE CASES
705
+ // ============================================================================
706
+
707
+ describe('Security Edge Cases', () => {
708
+ let protocol: MdashProtocol;
709
+
710
+ beforeAll(async () => {
711
+ protocol = createMdash({ sealKey: TEST_SEAL_KEY });
712
+ await protocol.initialize();
713
+ });
714
+
715
+ it('should reject empty seal key', async () => {
716
+ // P5 SECURITY: Minimum key length enforced
717
+ await expect(async () => {
718
+ const badProtocol = createMdash({ sealKey: '' });
719
+ await badProtocol.initialize();
720
+ }).rejects.toThrow('Seal key must be at least 32 characters');
721
+ });
722
+
723
+ it('should reject short seal key', async () => {
724
+ await expect(async () => {
725
+ const badProtocol = createMdash({ sealKey: 'short-key' });
726
+ await badProtocol.initialize();
727
+ }).rejects.toThrow('Seal key must be at least 32 characters');
728
+ });
729
+
730
+ it('should handle very long agent IDs', async () => {
731
+ const longAgentId = 'a'.repeat(10000);
732
+
733
+ const warrant = await protocol.prestageWarrant({
734
+ agentId: longAgentId,
735
+ policyId: 'financial-transfer-v2',
736
+ tier: 'T2',
737
+ constraints: { maxAmount: 1000 },
738
+ durationMs: 60000,
739
+ issuedBy: 'test@example.com',
740
+ });
741
+
742
+ expect(warrant.agent_id).toBe(longAgentId);
743
+ });
744
+
745
+ it('should handle special characters in policy ID', async () => {
746
+ // Note: This will fail because policy doesn't exist, but should not crash
747
+ await protocol.prestageWarrant({
748
+ agentId: 'special-policy-agent',
749
+ policyId: '../../../etc/passwd',
750
+ tier: 'T2',
751
+ constraints: { maxAmount: 1000 },
752
+ durationMs: 60000,
753
+ issuedBy: 'test@example.com',
754
+ });
755
+
756
+ // Physics validation will fail due to unknown policy
757
+ await expect(
758
+ protocol.execute({
759
+ agentId: 'special-policy-agent',
760
+ action: 'transfer',
761
+ actionParams: { amount: 100 },
762
+ execute: async () => ({ success: true }),
763
+ })
764
+ ).rejects.toThrow();
765
+ });
766
+
767
+ it('should use constant-time comparison for seals', async () => {
768
+ const key = await deriveKey(TEST_SEAL_KEY);
769
+ const data = { test: 'constant-time' };
770
+ const seal = await hmacSeal(data, key);
771
+
772
+ // This test documents that we use Web Crypto's verify,
773
+ // which is constant-time by implementation
774
+ const start1 = performance.now();
775
+ await hmacSeal(data, key); // Matching seal
776
+ const time1 = performance.now() - start1;
777
+
778
+ const start2 = performance.now();
779
+ await hmacSeal({ test: 'different' }, key); // Different seal
780
+ const time2 = performance.now() - start2;
781
+
782
+ // Times should be similar (within 10x, accounting for noise)
783
+ // This is a weak test but documents intent
784
+ expect(Math.abs(time1 - time2)).toBeLessThan(Math.max(time1, time2) * 10);
785
+ });
786
+ });