@teneo-protocol/sdk 1.0.0
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/.dockerignore +14 -0
- package/.env.test.example +14 -0
- package/.eslintrc.json +26 -0
- package/.github/workflows/claude-code-review.yml +78 -0
- package/.github/workflows/claude-reviewer.yml +64 -0
- package/.github/workflows/publish-npm.yml +38 -0
- package/.github/workflows/push-to-main.yml +23 -0
- package/.node-version +1 -0
- package/.prettierrc +11 -0
- package/Dockerfile +25 -0
- package/LICENCE +661 -0
- package/README.md +709 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +45 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/websocket-client.d.ts +261 -0
- package/dist/core/websocket-client.d.ts.map +1 -0
- package/dist/core/websocket-client.js +875 -0
- package/dist/core/websocket-client.js.map +1 -0
- package/dist/formatters/response-formatter.d.ts +354 -0
- package/dist/formatters/response-formatter.d.ts.map +1 -0
- package/dist/formatters/response-formatter.js +575 -0
- package/dist/formatters/response-formatter.js.map +1 -0
- package/dist/handlers/message-handler-registry.d.ts +155 -0
- package/dist/handlers/message-handler-registry.d.ts.map +1 -0
- package/dist/handlers/message-handler-registry.js +216 -0
- package/dist/handlers/message-handler-registry.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
- package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
- package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
- package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
- package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
- package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/base-handler.js +83 -0
- package/dist/handlers/message-handlers/base-handler.js.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.js +47 -0
- package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.js +29 -0
- package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/index.d.ts +28 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -0
- package/dist/handlers/message-handlers/index.js +100 -0
- package/dist/handlers/message-handlers/index.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.js +44 -0
- package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/types.d.ts +51 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -0
- package/dist/handlers/message-handlers/types.js +7 -0
- package/dist/handlers/message-handlers/types.js.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
- package/dist/handlers/webhook-handler.d.ts +202 -0
- package/dist/handlers/webhook-handler.d.ts.map +1 -0
- package/dist/handlers/webhook-handler.js +511 -0
- package/dist/handlers/webhook-handler.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/agent-registry.d.ts +173 -0
- package/dist/managers/agent-registry.d.ts.map +1 -0
- package/dist/managers/agent-registry.js +310 -0
- package/dist/managers/agent-registry.js.map +1 -0
- package/dist/managers/connection-manager.d.ts +134 -0
- package/dist/managers/connection-manager.d.ts.map +1 -0
- package/dist/managers/connection-manager.js +176 -0
- package/dist/managers/connection-manager.js.map +1 -0
- package/dist/managers/index.d.ts +9 -0
- package/dist/managers/index.d.ts.map +1 -0
- package/dist/managers/index.js +16 -0
- package/dist/managers/index.js.map +1 -0
- package/dist/managers/message-router.d.ts +112 -0
- package/dist/managers/message-router.d.ts.map +1 -0
- package/dist/managers/message-router.js +260 -0
- package/dist/managers/message-router.js.map +1 -0
- package/dist/managers/room-manager.d.ts +165 -0
- package/dist/managers/room-manager.d.ts.map +1 -0
- package/dist/managers/room-manager.js +227 -0
- package/dist/managers/room-manager.js.map +1 -0
- package/dist/teneo-sdk.d.ts +703 -0
- package/dist/teneo-sdk.d.ts.map +1 -0
- package/dist/teneo-sdk.js +907 -0
- package/dist/teneo-sdk.js.map +1 -0
- package/dist/types/config.d.ts +1047 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +720 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/error-codes.d.ts +29 -0
- package/dist/types/error-codes.d.ts.map +1 -0
- package/dist/types/error-codes.js +41 -0
- package/dist/types/error-codes.js.map +1 -0
- package/dist/types/events.d.ts +616 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +261 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/health.d.ts +40 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +6 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +123 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +3734 -0
- package/dist/types/messages.d.ts.map +1 -0
- package/dist/types/messages.js +482 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/validation.d.ts +81 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +115 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utils/bounded-queue.d.ts +127 -0
- package/dist/utils/bounded-queue.d.ts.map +1 -0
- package/dist/utils/bounded-queue.js +181 -0
- package/dist/utils/bounded-queue.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +141 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +215 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/deduplication-cache.d.ts +110 -0
- package/dist/utils/deduplication-cache.d.ts.map +1 -0
- package/dist/utils/deduplication-cache.js +177 -0
- package/dist/utils/deduplication-cache.js.map +1 -0
- package/dist/utils/event-waiter.d.ts +101 -0
- package/dist/utils/event-waiter.d.ts.map +1 -0
- package/dist/utils/event-waiter.js +118 -0
- package/dist/utils/event-waiter.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +72 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +122 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +190 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry-policy.d.ts +191 -0
- package/dist/utils/retry-policy.d.ts.map +1 -0
- package/dist/utils/retry-policy.js +225 -0
- package/dist/utils/retry-policy.js.map +1 -0
- package/dist/utils/secure-private-key.d.ts +113 -0
- package/dist/utils/secure-private-key.d.ts.map +1 -0
- package/dist/utils/secure-private-key.js +188 -0
- package/dist/utils/secure-private-key.js.map +1 -0
- package/dist/utils/signature-verifier.d.ts +143 -0
- package/dist/utils/signature-verifier.d.ts.map +1 -0
- package/dist/utils/signature-verifier.js +238 -0
- package/dist/utils/signature-verifier.js.map +1 -0
- package/dist/utils/ssrf-validator.d.ts +36 -0
- package/dist/utils/ssrf-validator.d.ts.map +1 -0
- package/dist/utils/ssrf-validator.js +195 -0
- package/dist/utils/ssrf-validator.js.map +1 -0
- package/examples/.env.example +17 -0
- package/examples/basic-usage.ts +211 -0
- package/examples/production-dashboard/.env.example +153 -0
- package/examples/production-dashboard/package.json +39 -0
- package/examples/production-dashboard/public/dashboard.html +642 -0
- package/examples/production-dashboard/server.ts +753 -0
- package/examples/webhook-integration.ts +239 -0
- package/examples/x-influencer-battle-redesign.html +1065 -0
- package/examples/x-influencer-battle-server.ts +217 -0
- package/examples/x-influencer-battle.html +787 -0
- package/package.json +65 -0
- package/src/constants.ts +43 -0
- package/src/core/websocket-client.test.ts +512 -0
- package/src/core/websocket-client.ts +1056 -0
- package/src/formatters/response-formatter.test.ts +571 -0
- package/src/formatters/response-formatter.ts +677 -0
- package/src/handlers/message-handler-registry.ts +239 -0
- package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
- package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
- package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
- package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
- package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
- package/src/handlers/message-handlers/base-handler.ts +101 -0
- package/src/handlers/message-handlers/challenge-handler.ts +57 -0
- package/src/handlers/message-handlers/error-message-handler.ts +27 -0
- package/src/handlers/message-handlers/index.ts +77 -0
- package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
- package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
- package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
- package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
- package/src/handlers/message-handlers/task-response-handler.ts +45 -0
- package/src/handlers/message-handlers/types.ts +77 -0
- package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
- package/src/handlers/webhook-handler.test.ts +789 -0
- package/src/handlers/webhook-handler.ts +576 -0
- package/src/index.ts +269 -0
- package/src/managers/agent-registry.test.ts +466 -0
- package/src/managers/agent-registry.ts +347 -0
- package/src/managers/connection-manager.ts +195 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/message-router.ts +349 -0
- package/src/managers/room-manager.ts +248 -0
- package/src/teneo-sdk.ts +1022 -0
- package/src/types/config.test.ts +325 -0
- package/src/types/config.ts +799 -0
- package/src/types/error-codes.ts +44 -0
- package/src/types/events.test.ts +302 -0
- package/src/types/events.ts +382 -0
- package/src/types/health.ts +46 -0
- package/src/types/index.ts +199 -0
- package/src/types/messages.test.ts +660 -0
- package/src/types/messages.ts +570 -0
- package/src/types/validation.ts +123 -0
- package/src/utils/bounded-queue.test.ts +356 -0
- package/src/utils/bounded-queue.ts +205 -0
- package/src/utils/circuit-breaker.test.ts +394 -0
- package/src/utils/circuit-breaker.ts +262 -0
- package/src/utils/deduplication-cache.test.ts +380 -0
- package/src/utils/deduplication-cache.ts +198 -0
- package/src/utils/event-waiter.test.ts +381 -0
- package/src/utils/event-waiter.ts +172 -0
- package/src/utils/index.ts +74 -0
- package/src/utils/logger.ts +87 -0
- package/src/utils/rate-limiter.test.ts +341 -0
- package/src/utils/rate-limiter.ts +211 -0
- package/src/utils/retry-policy.test.ts +558 -0
- package/src/utils/retry-policy.ts +272 -0
- package/src/utils/secure-private-key.test.ts +356 -0
- package/src/utils/secure-private-key.ts +205 -0
- package/src/utils/signature-verifier.test.ts +464 -0
- package/src/utils/signature-verifier.ts +298 -0
- package/src/utils/ssrf-validator.test.ts +372 -0
- package/src/utils/ssrf-validator.ts +224 -0
- package/tests/integration/real-server.test.ts +740 -0
- package/tests/integration/websocket.test.ts +381 -0
- package/tests/integration-setup.ts +16 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +32 -0
- package/vitest.config.ts +42 -0
- package/vitest.integration.config.ts +23 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure private key storage with in-memory encryption
|
|
3
|
+
* Addresses SEC-3: Private Key Exposure Risk
|
|
4
|
+
*
|
|
5
|
+
* This class encrypts private keys in memory using AES-256-GCM to prevent
|
|
6
|
+
* exposure through memory dumps, heap snapshots, or accidental logging.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const secureKey = new SecurePrivateKey(privateKey);
|
|
11
|
+
*
|
|
12
|
+
* // Use the key temporarily for signing
|
|
13
|
+
* const signature = secureKey.use((key) => {
|
|
14
|
+
* return signMessage(key);
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Clean up when done
|
|
18
|
+
* secureKey.destroy();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import crypto from 'crypto';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Securely stores and manages an encrypted private key in memory.
|
|
26
|
+
*
|
|
27
|
+
* The key is encrypted immediately upon construction and only decrypted
|
|
28
|
+
* temporarily when needed for operations like signing. Decrypted keys
|
|
29
|
+
* are zeroed out immediately after use.
|
|
30
|
+
*/
|
|
31
|
+
export class SecurePrivateKey {
|
|
32
|
+
private encrypted: Buffer;
|
|
33
|
+
private encryptionKey: Buffer;
|
|
34
|
+
private destroyed = false;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates a new secure private key storage.
|
|
38
|
+
* The provided private key is encrypted immediately and the original
|
|
39
|
+
* string becomes eligible for garbage collection.
|
|
40
|
+
*
|
|
41
|
+
* @param privateKey - The private key to encrypt and store securely
|
|
42
|
+
* @throws {Error} If private key is empty or invalid
|
|
43
|
+
*/
|
|
44
|
+
constructor(privateKey: string) {
|
|
45
|
+
if (!privateKey || typeof privateKey !== 'string') {
|
|
46
|
+
throw new Error('Private key must be a non-empty string');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Generate a random encryption key for AES-256
|
|
50
|
+
this.encryptionKey = crypto.randomBytes(32);
|
|
51
|
+
|
|
52
|
+
// Encrypt the private key immediately
|
|
53
|
+
this.encrypted = this.encrypt(privateKey);
|
|
54
|
+
|
|
55
|
+
// Original privateKey string will be garbage collected
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Temporarily decrypts the private key and passes it to the provided function.
|
|
60
|
+
* The decrypted key is automatically zeroed out after the function completes,
|
|
61
|
+
* whether it succeeds or throws an error.
|
|
62
|
+
*
|
|
63
|
+
* This is the only way to access the decrypted private key, ensuring minimal
|
|
64
|
+
* exposure time in plaintext.
|
|
65
|
+
*
|
|
66
|
+
* @template T - The return type of the function
|
|
67
|
+
* @param fn - Function that uses the decrypted private key
|
|
68
|
+
* @returns The result of the function
|
|
69
|
+
* @throws {Error} If the key has been destroyed
|
|
70
|
+
* @throws Any error thrown by the provided function
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const account = secureKey.use((key) => privateKeyToAccount(key));
|
|
75
|
+
* const signature = secureKey.use((key) => account.signMessage(key));
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
public use<T>(fn: (key: string) => T): T {
|
|
79
|
+
this.checkNotDestroyed();
|
|
80
|
+
|
|
81
|
+
const decrypted = this.decrypt();
|
|
82
|
+
try {
|
|
83
|
+
return fn(decrypted);
|
|
84
|
+
} finally {
|
|
85
|
+
// Zero out the decrypted string in memory
|
|
86
|
+
// Note: This is best-effort as JavaScript strings are immutable
|
|
87
|
+
// but we overwrite the backing buffer
|
|
88
|
+
this.zeroOutString(decrypted);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Destroys this secure key by zeroing out all sensitive buffers.
|
|
94
|
+
* After calling destroy(), this instance can no longer be used.
|
|
95
|
+
*
|
|
96
|
+
* This should be called when the SDK is disconnected or the key
|
|
97
|
+
* is no longer needed to prevent memory exposure.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* secureKey.destroy();
|
|
102
|
+
* // secureKey.use(...) will now throw an error
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
public destroy(): void {
|
|
106
|
+
if (this.destroyed) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Zero out all sensitive buffers
|
|
111
|
+
this.encryptionKey.fill(0);
|
|
112
|
+
this.encrypted.fill(0);
|
|
113
|
+
|
|
114
|
+
this.destroyed = true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Checks if this secure key has been destroyed.
|
|
119
|
+
*
|
|
120
|
+
* @returns True if destroyed, false otherwise
|
|
121
|
+
*/
|
|
122
|
+
public isDestroyed(): boolean {
|
|
123
|
+
return this.destroyed;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Encrypts the private key using AES-256-GCM.
|
|
128
|
+
*
|
|
129
|
+
* The encrypted buffer format is: [IV (16 bytes) | Auth Tag (16 bytes) | Ciphertext]
|
|
130
|
+
*
|
|
131
|
+
* @param data - The private key to encrypt
|
|
132
|
+
* @returns Buffer containing IV + auth tag + encrypted data
|
|
133
|
+
*/
|
|
134
|
+
private encrypt(data: string): Buffer {
|
|
135
|
+
// Generate random initialization vector
|
|
136
|
+
const iv = crypto.randomBytes(16);
|
|
137
|
+
|
|
138
|
+
// Create cipher
|
|
139
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', this.encryptionKey, iv);
|
|
140
|
+
|
|
141
|
+
// Encrypt the data
|
|
142
|
+
const encrypted = Buffer.concat([
|
|
143
|
+
cipher.update(data, 'utf8'),
|
|
144
|
+
cipher.final()
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
// Get authentication tag for integrity verification
|
|
148
|
+
const authTag = cipher.getAuthTag();
|
|
149
|
+
|
|
150
|
+
// Combine IV + authTag + encrypted data
|
|
151
|
+
return Buffer.concat([iv, authTag, encrypted]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Decrypts the stored private key.
|
|
156
|
+
*
|
|
157
|
+
* @returns The decrypted private key as a string
|
|
158
|
+
* @throws {Error} If decryption fails (tampered data or wrong key)
|
|
159
|
+
*/
|
|
160
|
+
private decrypt(): string {
|
|
161
|
+
// Extract components from encrypted buffer
|
|
162
|
+
const iv = this.encrypted.subarray(0, 16);
|
|
163
|
+
const authTag = this.encrypted.subarray(16, 32);
|
|
164
|
+
const ciphertext = this.encrypted.subarray(32);
|
|
165
|
+
|
|
166
|
+
// Create decipher
|
|
167
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', this.encryptionKey, iv);
|
|
168
|
+
decipher.setAuthTag(authTag);
|
|
169
|
+
|
|
170
|
+
// Decrypt the data
|
|
171
|
+
const decrypted = Buffer.concat([
|
|
172
|
+
decipher.update(ciphertext),
|
|
173
|
+
decipher.final()
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
return decrypted.toString('utf8');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Best-effort attempt to zero out a string in memory.
|
|
181
|
+
* JavaScript strings are immutable, but we can try to overwrite
|
|
182
|
+
* the backing buffer if it exists.
|
|
183
|
+
*
|
|
184
|
+
* @param str - The string to zero out
|
|
185
|
+
*/
|
|
186
|
+
private zeroOutString(str: string): void {
|
|
187
|
+
// Convert to buffer and zero it out
|
|
188
|
+
const buffer = Buffer.from(str, 'utf8');
|
|
189
|
+
buffer.fill(0);
|
|
190
|
+
|
|
191
|
+
// Note: The original string object may still exist in memory
|
|
192
|
+
// until garbage collected, but this reduces the attack surface
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Checks if this instance has been destroyed and throws if so.
|
|
197
|
+
*
|
|
198
|
+
* @throws {Error} If the key has been destroyed
|
|
199
|
+
*/
|
|
200
|
+
private checkNotDestroyed(): void {
|
|
201
|
+
if (this.destroyed) {
|
|
202
|
+
throw new Error('SecurePrivateKey has been destroyed and can no longer be used');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Signature Verifier
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
6
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
7
|
+
import { SignatureVerifier } from "./signature-verifier";
|
|
8
|
+
import { BaseMessage, createUserMessage } from "../types";
|
|
9
|
+
|
|
10
|
+
describe("SignatureVerifier", () => {
|
|
11
|
+
// Test accounts
|
|
12
|
+
const testAccount1 = privateKeyToAccount('0x1234567890123456789012345678901234567890123456789012345678901234');
|
|
13
|
+
const testAccount2 = privateKeyToAccount('0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd');
|
|
14
|
+
|
|
15
|
+
describe("constructor", () => {
|
|
16
|
+
it("should create verifier with default options", () => {
|
|
17
|
+
const verifier = new SignatureVerifier();
|
|
18
|
+
const options = verifier.getOptions();
|
|
19
|
+
|
|
20
|
+
expect(options.trustedAddresses).toEqual([]);
|
|
21
|
+
expect(options.requireSignaturesFor).toEqual([]);
|
|
22
|
+
expect(options.strictMode).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should create verifier with custom options", () => {
|
|
26
|
+
const verifier = new SignatureVerifier({
|
|
27
|
+
trustedAddresses: [testAccount1.address],
|
|
28
|
+
requireSignaturesFor: ['task_response', 'agent_selected'],
|
|
29
|
+
strictMode: true
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const options = verifier.getOptions();
|
|
33
|
+
expect(options.trustedAddresses).toEqual([testAccount1.address]);
|
|
34
|
+
expect(options.requireSignaturesFor).toEqual(['task_response', 'agent_selected']);
|
|
35
|
+
expect(options.strictMode).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("getSignableContent", () => {
|
|
40
|
+
it("should exclude signature and publicKey fields", () => {
|
|
41
|
+
const verifier = new SignatureVerifier();
|
|
42
|
+
const message: BaseMessage = {
|
|
43
|
+
type: 'message',
|
|
44
|
+
content: 'Hello',
|
|
45
|
+
from: 'agent1',
|
|
46
|
+
signature: '0xsignature',
|
|
47
|
+
publicKey: '0xpublickey',
|
|
48
|
+
timestamp: '2024-01-01T00:00:00Z'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const signable = verifier.getSignableContent(message);
|
|
52
|
+
|
|
53
|
+
expect(signable).not.toHaveProperty('signature');
|
|
54
|
+
expect(signable).not.toHaveProperty('publicKey');
|
|
55
|
+
expect(signable).not.toHaveProperty('id');
|
|
56
|
+
expect(signable).toHaveProperty('type');
|
|
57
|
+
expect(signable).toHaveProperty('content');
|
|
58
|
+
expect(signable).toHaveProperty('from');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should exclude undefined fields", () => {
|
|
62
|
+
const verifier = new SignatureVerifier();
|
|
63
|
+
const message: BaseMessage = {
|
|
64
|
+
type: 'message',
|
|
65
|
+
content: 'Hello',
|
|
66
|
+
from: undefined,
|
|
67
|
+
room: 'general'
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const signable = verifier.getSignableContent(message);
|
|
71
|
+
|
|
72
|
+
expect(signable).not.toHaveProperty('from');
|
|
73
|
+
expect(signable).toHaveProperty('type');
|
|
74
|
+
expect(signable).toHaveProperty('content');
|
|
75
|
+
expect(signable).toHaveProperty('room');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("createMessageHash", () => {
|
|
80
|
+
it("should create consistent hashes for same content", () => {
|
|
81
|
+
const verifier = new SignatureVerifier();
|
|
82
|
+
const content = { type: 'message', content: 'Hello' };
|
|
83
|
+
|
|
84
|
+
const hash1 = verifier.createMessageHash(content);
|
|
85
|
+
const hash2 = verifier.createMessageHash(content);
|
|
86
|
+
|
|
87
|
+
expect(hash1).toBe(hash2);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should create different hashes for different content", () => {
|
|
91
|
+
const verifier = new SignatureVerifier();
|
|
92
|
+
const content1 = { type: 'message', content: 'Hello' };
|
|
93
|
+
const content2 = { type: 'message', content: 'World' };
|
|
94
|
+
|
|
95
|
+
const hash1 = verifier.createMessageHash(content1);
|
|
96
|
+
const hash2 = verifier.createMessageHash(content2);
|
|
97
|
+
|
|
98
|
+
expect(hash1).not.toBe(hash2);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should create same hash regardless of key order", () => {
|
|
102
|
+
const verifier = new SignatureVerifier();
|
|
103
|
+
const content1 = { type: 'message', content: 'Hello', from: 'agent' };
|
|
104
|
+
const content2 = { from: 'agent', content: 'Hello', type: 'message' };
|
|
105
|
+
|
|
106
|
+
const hash1 = verifier.createMessageHash(content1);
|
|
107
|
+
const hash2 = verifier.createMessageHash(content2);
|
|
108
|
+
|
|
109
|
+
expect(hash1).toBe(hash2);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("isSignatureRequired", () => {
|
|
114
|
+
it("should return false when message type not in required list", () => {
|
|
115
|
+
const verifier = new SignatureVerifier({
|
|
116
|
+
requireSignaturesFor: ['task_response']
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(verifier.isSignatureRequired('message')).toBe(false);
|
|
120
|
+
expect(verifier.isSignatureRequired('ping')).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should return true when message type is in required list", () => {
|
|
124
|
+
const verifier = new SignatureVerifier({
|
|
125
|
+
requireSignaturesFor: ['task_response', 'agent_selected']
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(verifier.isSignatureRequired('task_response')).toBe(true);
|
|
129
|
+
expect(verifier.isSignatureRequired('agent_selected')).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should return false when no required types configured", () => {
|
|
133
|
+
const verifier = new SignatureVerifier();
|
|
134
|
+
|
|
135
|
+
expect(verifier.isSignatureRequired('task_response')).toBe(false);
|
|
136
|
+
expect(verifier.isSignatureRequired('message')).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("isTrustedAddress", () => {
|
|
141
|
+
it("should return true when no whitelist configured", () => {
|
|
142
|
+
const verifier = new SignatureVerifier();
|
|
143
|
+
|
|
144
|
+
expect(verifier.isTrustedAddress(testAccount1.address)).toBe(true);
|
|
145
|
+
expect(verifier.isTrustedAddress(testAccount2.address)).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should return true for addresses in whitelist", () => {
|
|
149
|
+
const verifier = new SignatureVerifier({
|
|
150
|
+
trustedAddresses: [testAccount1.address]
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(verifier.isTrustedAddress(testAccount1.address)).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should return false for addresses not in whitelist", () => {
|
|
157
|
+
const verifier = new SignatureVerifier({
|
|
158
|
+
trustedAddresses: [testAccount1.address]
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(verifier.isTrustedAddress(testAccount2.address)).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should be case-insensitive", () => {
|
|
165
|
+
const verifier = new SignatureVerifier({
|
|
166
|
+
trustedAddresses: [testAccount1.address.toLowerCase()]
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(verifier.isTrustedAddress(testAccount1.address.toUpperCase())).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("verify - missing signature", () => {
|
|
174
|
+
it("should pass when signature missing and not required", async () => {
|
|
175
|
+
const verifier = new SignatureVerifier({
|
|
176
|
+
strictMode: false
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const message: BaseMessage = {
|
|
180
|
+
type: 'ping'
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const result = await verifier.verify(message);
|
|
184
|
+
|
|
185
|
+
expect(result.valid).toBe(true);
|
|
186
|
+
expect(result.signatureMissing).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should fail when signature missing but required for message type", async () => {
|
|
190
|
+
const verifier = new SignatureVerifier({
|
|
191
|
+
requireSignaturesFor: ['task_response']
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const message: BaseMessage = {
|
|
195
|
+
type: 'task_response',
|
|
196
|
+
content: 'Result'
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const result = await verifier.verify(message);
|
|
200
|
+
|
|
201
|
+
expect(result.valid).toBe(false);
|
|
202
|
+
expect(result.signatureMissing).toBe(true);
|
|
203
|
+
expect(result.reason).toContain('required');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("should fail when signature missing and strictMode enabled", async () => {
|
|
207
|
+
const verifier = new SignatureVerifier({
|
|
208
|
+
strictMode: true
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const message: BaseMessage = {
|
|
212
|
+
type: 'message',
|
|
213
|
+
content: 'Hello'
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const result = await verifier.verify(message);
|
|
217
|
+
|
|
218
|
+
expect(result.valid).toBe(false);
|
|
219
|
+
expect(result.signatureMissing).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("verify - with signature", () => {
|
|
224
|
+
it("should verify valid signature", async () => {
|
|
225
|
+
const verifier = new SignatureVerifier();
|
|
226
|
+
|
|
227
|
+
const message: BaseMessage = {
|
|
228
|
+
type: 'message',
|
|
229
|
+
content: 'Hello',
|
|
230
|
+
from: 'agent1',
|
|
231
|
+
timestamp: '2024-01-01T00:00:00Z',
|
|
232
|
+
publicKey: testAccount1.address
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const signableContent = verifier.getSignableContent(message);
|
|
236
|
+
const messageHash = verifier.createMessageHash(signableContent);
|
|
237
|
+
const signature = await testAccount1.signMessage({ message: messageHash });
|
|
238
|
+
|
|
239
|
+
const messageWithSignature: BaseMessage = {
|
|
240
|
+
...message,
|
|
241
|
+
signature
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const result = await verifier.verify(messageWithSignature);
|
|
245
|
+
|
|
246
|
+
expect(result.valid).toBe(true);
|
|
247
|
+
expect(result.signatureMissing).toBe(false);
|
|
248
|
+
expect(result.recoveredAddress).toBe(testAccount1.address);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("should reject invalid signature", async () => {
|
|
252
|
+
const verifier = new SignatureVerifier();
|
|
253
|
+
|
|
254
|
+
const message: BaseMessage = {
|
|
255
|
+
type: 'message',
|
|
256
|
+
content: 'Hello',
|
|
257
|
+
publicKey: testAccount1.address,
|
|
258
|
+
signature: '0xinvalidsignature1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012'
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const result = await verifier.verify(message);
|
|
262
|
+
|
|
263
|
+
expect(result.valid).toBe(false);
|
|
264
|
+
expect(result.signatureMissing).toBe(false);
|
|
265
|
+
expect(result.reason).toContain('error');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should reject signature from wrong account", async () => {
|
|
269
|
+
const verifier = new SignatureVerifier();
|
|
270
|
+
|
|
271
|
+
const message: BaseMessage = {
|
|
272
|
+
type: 'message',
|
|
273
|
+
content: 'Hello',
|
|
274
|
+
publicKey: testAccount1.address
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const signableContent = verifier.getSignableContent(message);
|
|
278
|
+
const messageHash = verifier.createMessageHash(signableContent);
|
|
279
|
+
|
|
280
|
+
// Sign with account2 but verify against account1
|
|
281
|
+
const signature = await testAccount2.signMessage({ message: messageHash });
|
|
282
|
+
|
|
283
|
+
const messageWithSignature: BaseMessage = {
|
|
284
|
+
...message,
|
|
285
|
+
signature
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const result = await verifier.verify(messageWithSignature);
|
|
289
|
+
|
|
290
|
+
expect(result.valid).toBe(false);
|
|
291
|
+
expect(result.reason).toContain('does not match');
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe("verify - whitelist", () => {
|
|
296
|
+
it("should accept message from trusted address", async () => {
|
|
297
|
+
const verifier = new SignatureVerifier({
|
|
298
|
+
trustedAddresses: [testAccount1.address]
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const message: BaseMessage = {
|
|
302
|
+
type: 'message',
|
|
303
|
+
content: 'Hello',
|
|
304
|
+
publicKey: testAccount1.address
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const signableContent = verifier.getSignableContent(message);
|
|
308
|
+
const messageHash = verifier.createMessageHash(signableContent);
|
|
309
|
+
const signature = await testAccount1.signMessage({ message: messageHash });
|
|
310
|
+
|
|
311
|
+
const messageWithSignature: BaseMessage = {
|
|
312
|
+
...message,
|
|
313
|
+
signature
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const result = await verifier.verify(messageWithSignature);
|
|
317
|
+
|
|
318
|
+
expect(result.valid).toBe(true);
|
|
319
|
+
expect(result.isTrusted).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("should reject message from untrusted address", async () => {
|
|
323
|
+
const verifier = new SignatureVerifier({
|
|
324
|
+
trustedAddresses: [testAccount1.address]
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const message: BaseMessage = {
|
|
328
|
+
type: 'message',
|
|
329
|
+
content: 'Hello',
|
|
330
|
+
publicKey: testAccount2.address
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const signableContent = verifier.getSignableContent(message);
|
|
334
|
+
const messageHash = verifier.createMessageHash(signableContent);
|
|
335
|
+
const signature = await testAccount2.signMessage({ message: messageHash });
|
|
336
|
+
|
|
337
|
+
const messageWithSignature: BaseMessage = {
|
|
338
|
+
...message,
|
|
339
|
+
signature
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const result = await verifier.verify(messageWithSignature);
|
|
343
|
+
|
|
344
|
+
expect(result.valid).toBe(false);
|
|
345
|
+
expect(result.isTrusted).toBe(false);
|
|
346
|
+
expect(result.reason).toContain('not in trusted whitelist');
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
describe("verify - address sources", () => {
|
|
351
|
+
it("should use publicKey field for verification", async () => {
|
|
352
|
+
const verifier = new SignatureVerifier();
|
|
353
|
+
|
|
354
|
+
const message: BaseMessage = {
|
|
355
|
+
type: 'message',
|
|
356
|
+
content: 'Hello',
|
|
357
|
+
publicKey: testAccount1.address
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const signableContent = verifier.getSignableContent(message);
|
|
361
|
+
const messageHash = verifier.createMessageHash(signableContent);
|
|
362
|
+
const signature = await testAccount1.signMessage({ message: messageHash });
|
|
363
|
+
|
|
364
|
+
const messageWithSignature: BaseMessage = {
|
|
365
|
+
...message,
|
|
366
|
+
signature
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const result = await verifier.verify(messageWithSignature);
|
|
370
|
+
|
|
371
|
+
expect(result.valid).toBe(true);
|
|
372
|
+
expect(result.recoveredAddress).toBe(testAccount1.address);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("should fallback to from field if address-like", async () => {
|
|
376
|
+
const verifier = new SignatureVerifier();
|
|
377
|
+
|
|
378
|
+
const message: BaseMessage = {
|
|
379
|
+
type: 'message',
|
|
380
|
+
content: 'Hello',
|
|
381
|
+
from: testAccount1.address
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const signableContent = verifier.getSignableContent(message);
|
|
385
|
+
const messageHash = verifier.createMessageHash(signableContent);
|
|
386
|
+
const signature = await testAccount1.signMessage({ message: messageHash });
|
|
387
|
+
|
|
388
|
+
const messageWithSignature: BaseMessage = {
|
|
389
|
+
...message,
|
|
390
|
+
signature
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const result = await verifier.verify(messageWithSignature);
|
|
394
|
+
|
|
395
|
+
expect(result.valid).toBe(true);
|
|
396
|
+
expect(result.recoveredAddress).toBe(testAccount1.address);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("should fail if no valid address available", async () => {
|
|
400
|
+
const verifier = new SignatureVerifier();
|
|
401
|
+
|
|
402
|
+
const message: BaseMessage = {
|
|
403
|
+
type: 'message',
|
|
404
|
+
content: 'Hello',
|
|
405
|
+
from: 'agent-name-not-address',
|
|
406
|
+
signature: '0xsignature'
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const result = await verifier.verify(message);
|
|
410
|
+
|
|
411
|
+
expect(result.valid).toBe(false);
|
|
412
|
+
expect(result.reason).toContain('No address available');
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe("updateOptions", () => {
|
|
417
|
+
it("should update trusted addresses", () => {
|
|
418
|
+
const verifier = new SignatureVerifier();
|
|
419
|
+
|
|
420
|
+
verifier.updateOptions({
|
|
421
|
+
trustedAddresses: [testAccount1.address]
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const options = verifier.getOptions();
|
|
425
|
+
expect(options.trustedAddresses).toEqual([testAccount1.address]);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it("should update required message types", () => {
|
|
429
|
+
const verifier = new SignatureVerifier();
|
|
430
|
+
|
|
431
|
+
verifier.updateOptions({
|
|
432
|
+
requireSignaturesFor: ['task_response']
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
expect(verifier.isSignatureRequired('task_response')).toBe(true);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("should update strict mode", () => {
|
|
439
|
+
const verifier = new SignatureVerifier({ strictMode: false });
|
|
440
|
+
|
|
441
|
+
verifier.updateOptions({
|
|
442
|
+
strictMode: true
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const options = verifier.getOptions();
|
|
446
|
+
expect(options.strictMode).toBe(true);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it("should merge with existing options", () => {
|
|
450
|
+
const verifier = new SignatureVerifier({
|
|
451
|
+
trustedAddresses: [testAccount1.address],
|
|
452
|
+
strictMode: false
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
verifier.updateOptions({
|
|
456
|
+
strictMode: true
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const options = verifier.getOptions();
|
|
460
|
+
expect(options.trustedAddresses).toEqual([testAccount1.address]);
|
|
461
|
+
expect(options.strictMode).toBe(true);
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
});
|