@agirails/sdk 2.5.3 → 2.5.5
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/ACTPClient.d.ts +18 -0
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +72 -23
- package/dist/ACTPClient.js.map +1 -1
- package/dist/adapters/BasicAdapter.d.ts +15 -0
- package/dist/adapters/BasicAdapter.d.ts.map +1 -1
- package/dist/adapters/BasicAdapter.js +33 -4
- package/dist/adapters/BasicAdapter.js.map +1 -1
- package/dist/adapters/StandardAdapter.d.ts +20 -3
- package/dist/adapters/StandardAdapter.d.ts.map +1 -1
- package/dist/adapters/StandardAdapter.js +90 -12
- package/dist/adapters/StandardAdapter.js.map +1 -1
- package/dist/cli/commands/publish.js +16 -4
- package/dist/cli/commands/publish.js.map +1 -1
- package/dist/cli/commands/register.js +16 -4
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/tx.js +31 -3
- package/dist/cli/commands/tx.js.map +1 -1
- package/dist/config/networks.d.ts +10 -2
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +31 -22
- package/dist/config/networks.js.map +1 -1
- package/dist/level0/request.d.ts.map +1 -1
- package/dist/level0/request.js +2 -1
- package/dist/level0/request.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
- package/dist/runtime/BlockchainRuntime.js +11 -5
- package/dist/runtime/BlockchainRuntime.js.map +1 -1
- package/dist/utils/IPFSClient.d.ts +3 -1
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +27 -7
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/wallet/AutoWalletProvider.d.ts +11 -1
- package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
- package/dist/wallet/AutoWalletProvider.js +84 -19
- package/dist/wallet/AutoWalletProvider.js.map +1 -1
- package/dist/wallet/IWalletProvider.d.ts +34 -0
- package/dist/wallet/IWalletProvider.d.ts.map +1 -1
- package/dist/wallet/SmartWalletRouter.d.ts +128 -0
- package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
- package/dist/wallet/SmartWalletRouter.js +248 -0
- package/dist/wallet/SmartWalletRouter.js.map +1 -0
- package/dist/wallet/aa/DualNonceManager.d.ts +26 -1
- package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
- package/dist/wallet/aa/DualNonceManager.js +140 -6
- package/dist/wallet/aa/DualNonceManager.js.map +1 -1
- package/package.json +3 -6
- package/src/ACTPClient.ts +0 -1579
- package/src/abi/ACTPKernel.json +0 -1356
- package/src/abi/AgentRegistry.json +0 -915
- package/src/abi/ERC20.json +0 -40
- package/src/abi/EscrowVault.json +0 -134
- package/src/abi/IdentityRegistry.json +0 -316
- package/src/adapters/AdapterRegistry.ts +0 -173
- package/src/adapters/AdapterRouter.ts +0 -416
- package/src/adapters/BaseAdapter.ts +0 -498
- package/src/adapters/BasicAdapter.ts +0 -514
- package/src/adapters/IAdapter.ts +0 -292
- package/src/adapters/StandardAdapter.ts +0 -555
- package/src/adapters/X402Adapter.ts +0 -731
- package/src/adapters/index.ts +0 -60
- package/src/builders/DeliveryProofBuilder.ts +0 -327
- package/src/builders/QuoteBuilder.ts +0 -483
- package/src/builders/index.ts +0 -17
- package/src/cli/commands/balance.ts +0 -110
- package/src/cli/commands/batch.ts +0 -487
- package/src/cli/commands/config.ts +0 -231
- package/src/cli/commands/deploy-check.ts +0 -364
- package/src/cli/commands/deploy-env.ts +0 -120
- package/src/cli/commands/diff.ts +0 -141
- package/src/cli/commands/init.ts +0 -469
- package/src/cli/commands/mint.ts +0 -116
- package/src/cli/commands/pay.ts +0 -113
- package/src/cli/commands/publish.ts +0 -475
- package/src/cli/commands/pull.ts +0 -124
- package/src/cli/commands/register.ts +0 -247
- package/src/cli/commands/simulate.ts +0 -345
- package/src/cli/commands/time.ts +0 -302
- package/src/cli/commands/tx.ts +0 -448
- package/src/cli/commands/watch.ts +0 -211
- package/src/cli/index.ts +0 -134
- package/src/cli/utils/client.ts +0 -252
- package/src/cli/utils/config.ts +0 -389
- package/src/cli/utils/output.ts +0 -465
- package/src/cli/utils/wallet.ts +0 -109
- package/src/config/agirailsmd.ts +0 -262
- package/src/config/networks.ts +0 -275
- package/src/config/pendingPublish.ts +0 -237
- package/src/config/publishPipeline.ts +0 -359
- package/src/config/syncOperations.ts +0 -279
- package/src/erc8004/ERC8004Bridge.ts +0 -462
- package/src/erc8004/ReputationReporter.ts +0 -468
- package/src/erc8004/index.ts +0 -61
- package/src/errors/index.ts +0 -427
- package/src/index.ts +0 -364
- package/src/level0/Provider.ts +0 -117
- package/src/level0/ServiceDirectory.ts +0 -131
- package/src/level0/index.ts +0 -10
- package/src/level0/provide.ts +0 -132
- package/src/level0/request.ts +0 -432
- package/src/level1/Agent.ts +0 -1426
- package/src/level1/index.ts +0 -10
- package/src/level1/pricing/PriceCalculator.ts +0 -255
- package/src/level1/pricing/PricingStrategy.ts +0 -198
- package/src/level1/types/Job.ts +0 -179
- package/src/level1/types/Options.ts +0 -291
- package/src/level1/types/index.ts +0 -8
- package/src/protocol/ACTPKernel.ts +0 -808
- package/src/protocol/AgentRegistry.ts +0 -559
- package/src/protocol/DIDManager.ts +0 -629
- package/src/protocol/DIDResolver.ts +0 -554
- package/src/protocol/EASHelper.ts +0 -378
- package/src/protocol/EscrowVault.ts +0 -255
- package/src/protocol/EventMonitor.ts +0 -204
- package/src/protocol/MessageSigner.ts +0 -510
- package/src/protocol/ProofGenerator.ts +0 -339
- package/src/protocol/QuoteBuilder.ts +0 -15
- package/src/registry/AgentRegistryClient.ts +0 -202
- package/src/runtime/BlockchainRuntime.ts +0 -1015
- package/src/runtime/IACTPRuntime.ts +0 -306
- package/src/runtime/MockRuntime.ts +0 -1298
- package/src/runtime/MockStateManager.ts +0 -577
- package/src/runtime/index.ts +0 -25
- package/src/runtime/types/MockState.ts +0 -237
- package/src/storage/ArchiveBundleBuilder.ts +0 -561
- package/src/storage/ArweaveClient.ts +0 -946
- package/src/storage/FilebaseClient.ts +0 -790
- package/src/storage/index.ts +0 -96
- package/src/storage/types.ts +0 -348
- package/src/types/adapter.ts +0 -310
- package/src/types/agent.ts +0 -79
- package/src/types/did.ts +0 -223
- package/src/types/eip712.ts +0 -175
- package/src/types/erc8004.ts +0 -293
- package/src/types/escrow.ts +0 -27
- package/src/types/index.ts +0 -17
- package/src/types/message.ts +0 -145
- package/src/types/state.ts +0 -87
- package/src/types/transaction.ts +0 -69
- package/src/types/x402.ts +0 -251
- package/src/utils/ErrorRecoveryGuide.ts +0 -676
- package/src/utils/Helpers.ts +0 -688
- package/src/utils/IPFSClient.ts +0 -368
- package/src/utils/Logger.ts +0 -484
- package/src/utils/NonceManager.ts +0 -591
- package/src/utils/RateLimiter.ts +0 -534
- package/src/utils/ReceivedNonceTracker.ts +0 -567
- package/src/utils/SDKLifecycle.ts +0 -416
- package/src/utils/SecureNonce.ts +0 -78
- package/src/utils/Semaphore.ts +0 -276
- package/src/utils/UsedAttestationTracker.ts +0 -385
- package/src/utils/canonicalJson.ts +0 -38
- package/src/utils/circuitBreaker.ts +0 -324
- package/src/utils/computeTypeHash.ts +0 -48
- package/src/utils/fsSafe.ts +0 -80
- package/src/utils/index.ts +0 -80
- package/src/utils/retry.ts +0 -364
- package/src/utils/security.ts +0 -418
- package/src/utils/validation.ts +0 -540
- package/src/wallet/AutoWalletProvider.ts +0 -299
- package/src/wallet/EOAWalletProvider.ts +0 -69
- package/src/wallet/IWalletProvider.ts +0 -135
- package/src/wallet/aa/BundlerClient.ts +0 -274
- package/src/wallet/aa/DualNonceManager.ts +0 -173
- package/src/wallet/aa/PaymasterClient.ts +0 -174
- package/src/wallet/aa/TransactionBatcher.ts +0 -353
- package/src/wallet/aa/UserOpBuilder.ts +0 -246
- package/src/wallet/aa/constants.ts +0 -60
- package/src/wallet/keystore.ts +0 -240
package/src/utils/security.ts
DELETED
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Security Utilities for ACTP SDK
|
|
3
|
-
*
|
|
4
|
-
* SECURITY FIXES:
|
|
5
|
-
* - H-7: Constant-time string comparison (timing attack prevention)
|
|
6
|
-
* - H-6: Path traversal prevention
|
|
7
|
-
* - H-2: Input validation and sanitization
|
|
8
|
-
* - C-3: Safe JSON parsing with schema validation
|
|
9
|
-
*
|
|
10
|
-
* @module utils/security
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import * as crypto from 'crypto';
|
|
14
|
-
import * as path from 'path';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* H-7: Constant-time string comparison to prevent timing attacks
|
|
18
|
-
*
|
|
19
|
-
* Never use === for comparing signatures, hashes, or other security-sensitive strings
|
|
20
|
-
* as it can leak timing information that attackers can exploit.
|
|
21
|
-
*
|
|
22
|
-
* @param a - First string to compare
|
|
23
|
-
* @param b - Second string to compare
|
|
24
|
-
* @returns true if strings are equal, false otherwise
|
|
25
|
-
*/
|
|
26
|
-
export function timingSafeEqual(a: string, b: string): boolean {
|
|
27
|
-
if (typeof a !== 'string' || typeof b !== 'string') {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Convert to buffers for crypto.timingSafeEqual
|
|
32
|
-
const bufA = Buffer.from(a);
|
|
33
|
-
const bufB = Buffer.from(b);
|
|
34
|
-
|
|
35
|
-
// If lengths differ, still use timingSafeEqual to prevent timing leaks
|
|
36
|
-
if (bufA.length !== bufB.length) {
|
|
37
|
-
// Compare against a dummy buffer of the same length to maintain constant time
|
|
38
|
-
const dummy = Buffer.alloc(bufA.length);
|
|
39
|
-
crypto.timingSafeEqual(bufA, dummy);
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return crypto.timingSafeEqual(bufA, bufB);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* H-6: Validate and sanitize directory path to prevent path traversal
|
|
48
|
-
*
|
|
49
|
-
* Ensures that the provided path:
|
|
50
|
-
* 1. Does not contain '..' sequences
|
|
51
|
-
* 2. Resolves to a location within the allowed base directory
|
|
52
|
-
* 3. Does not follow symlinks (optional)
|
|
53
|
-
*
|
|
54
|
-
* @param requestedPath - The path to validate
|
|
55
|
-
* @param baseDirectory - The base directory to restrict paths to
|
|
56
|
-
* @returns Sanitized absolute path
|
|
57
|
-
* @throws Error if path is invalid or contains traversal attempts
|
|
58
|
-
*/
|
|
59
|
-
export function validatePath(requestedPath: string, baseDirectory: string): string {
|
|
60
|
-
// Check for null bytes (can be used to bypass security checks)
|
|
61
|
-
if (requestedPath.includes('\0')) {
|
|
62
|
-
throw new Error('Invalid path: null byte detected');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Normalize the path BEFORE checking for '..'
|
|
66
|
-
// This prevents tricks like '.../...//' or 'foo/../../../etc/passwd'
|
|
67
|
-
const normalized = path.normalize(requestedPath);
|
|
68
|
-
|
|
69
|
-
// Check for '..' sequences after normalization
|
|
70
|
-
if (normalized.includes('..')) {
|
|
71
|
-
throw new Error('Invalid path: path traversal detected (..)');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Resolve to absolute path
|
|
75
|
-
const absolute = path.resolve(baseDirectory, normalized);
|
|
76
|
-
|
|
77
|
-
// Ensure the resolved path is still within the base directory
|
|
78
|
-
// This prevents attacks like:
|
|
79
|
-
// - Symlink following to escape the directory
|
|
80
|
-
// - Absolute paths that ignore the base directory
|
|
81
|
-
const normalizedBase = path.resolve(baseDirectory);
|
|
82
|
-
if (!absolute.startsWith(normalizedBase + path.sep) && absolute !== normalizedBase) {
|
|
83
|
-
throw new Error(`Invalid path: resolved path '${absolute}' is outside base directory '${normalizedBase}'`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return absolute;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* H-2: Validate and sanitize service name
|
|
91
|
-
*
|
|
92
|
-
* Ensures service name:
|
|
93
|
-
* 1. Contains only safe characters (alphanumeric, dash, dot, underscore)
|
|
94
|
-
* 2. Does not exceed maximum length
|
|
95
|
-
* 3. Does not contain special characters that could cause injection
|
|
96
|
-
*
|
|
97
|
-
* @param serviceName - The service name to validate
|
|
98
|
-
* @returns Sanitized service name
|
|
99
|
-
* @throws Error if service name is invalid
|
|
100
|
-
*/
|
|
101
|
-
export function validateServiceName(serviceName: string): string {
|
|
102
|
-
if (!serviceName || typeof serviceName !== 'string') {
|
|
103
|
-
throw new Error('Invalid service name: must be a non-empty string');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Trim whitespace
|
|
107
|
-
const trimmed = serviceName.trim();
|
|
108
|
-
|
|
109
|
-
// Check length (max 256 chars)
|
|
110
|
-
if (trimmed.length === 0) {
|
|
111
|
-
throw new Error('Invalid service name: cannot be empty');
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (trimmed.length > 256) {
|
|
115
|
-
throw new Error('Invalid service name: exceeds maximum length of 256 characters');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Validate format: alphanumeric, dash, dot, underscore only
|
|
119
|
-
// This prevents injection attacks and ensures compatibility across systems
|
|
120
|
-
const validPattern = /^[a-zA-Z0-9._-]+$/;
|
|
121
|
-
if (!validPattern.test(trimmed)) {
|
|
122
|
-
throw new Error(
|
|
123
|
-
'Invalid service name: only alphanumeric characters, dots, dashes, and underscores are allowed'
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Prevent names that could cause issues
|
|
128
|
-
if (trimmed === '.' || trimmed === '..' || trimmed.startsWith('.')) {
|
|
129
|
-
throw new Error('Invalid service name: cannot start with a dot');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return trimmed;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* H-5: Validate Ethereum address format
|
|
137
|
-
*
|
|
138
|
-
* Ensures address:
|
|
139
|
-
* 1. Is a valid hex string
|
|
140
|
-
* 2. Has correct length (42 chars including '0x' prefix)
|
|
141
|
-
* 3. Uses valid checksum if provided (EIP-55)
|
|
142
|
-
*
|
|
143
|
-
* @param address - The Ethereum address to validate
|
|
144
|
-
* @returns true if address is valid, false otherwise
|
|
145
|
-
*/
|
|
146
|
-
export function isValidAddress(address: string): boolean {
|
|
147
|
-
if (!address || typeof address !== 'string') {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Must start with 0x
|
|
152
|
-
if (!address.startsWith('0x')) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Must be exactly 42 characters (0x + 40 hex chars)
|
|
157
|
-
if (address.length !== 42) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Must contain only valid hex characters
|
|
162
|
-
const hexPattern = /^0x[a-fA-F0-9]{40}$/;
|
|
163
|
-
if (!hexPattern.test(address)) {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Note: Full EIP-55 checksum validation would be more complex
|
|
168
|
-
// For now, we just validate format. In production, consider using ethers.utils.getAddress()
|
|
169
|
-
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* C-3: Safe JSON parsing with schema validation
|
|
175
|
-
*
|
|
176
|
-
* Prevents code injection and prototype pollution attacks by:
|
|
177
|
-
* 1. Safely parsing JSON with error handling
|
|
178
|
-
* 2. Validating the parsed object against an expected schema
|
|
179
|
-
* 3. Removing __proto__, constructor, and prototype properties
|
|
180
|
-
* 4. Returning only whitelisted fields
|
|
181
|
-
*
|
|
182
|
-
* @param jsonString - The JSON string to parse
|
|
183
|
-
* @param schema - Expected schema (object with field names and types)
|
|
184
|
-
* @returns Parsed and validated object, or null if invalid
|
|
185
|
-
*/
|
|
186
|
-
export function safeJSONParse<T = any>(
|
|
187
|
-
jsonString: string,
|
|
188
|
-
schema?: Record<string, string>
|
|
189
|
-
): T | null {
|
|
190
|
-
if (!jsonString || typeof jsonString !== 'string') {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// SECURITY FIX (C-3): Check JSON size to prevent DoS attacks
|
|
195
|
-
const MAX_JSON_SIZE = 1_000_000; // 1MB
|
|
196
|
-
if (jsonString.length > MAX_JSON_SIZE) {
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
let parsed: any;
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
// Basic JSON parsing
|
|
204
|
-
parsed = JSON.parse(jsonString);
|
|
205
|
-
} catch (error) {
|
|
206
|
-
// Invalid JSON
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Ensure we got an object (not a primitive or array at the top level)
|
|
211
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Remove dangerous properties that could be used for prototype pollution
|
|
216
|
-
const dangerous = ['__proto__', 'constructor', 'prototype'];
|
|
217
|
-
for (const key of dangerous) {
|
|
218
|
-
delete parsed[key];
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// If schema is provided, validate and whitelist
|
|
222
|
-
if (schema) {
|
|
223
|
-
const validated: Record<string, any> = {};
|
|
224
|
-
|
|
225
|
-
for (const [field, expectedType] of Object.entries(schema)) {
|
|
226
|
-
const value = parsed[field];
|
|
227
|
-
|
|
228
|
-
// Skip if field doesn't exist
|
|
229
|
-
if (value === undefined) {
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Type check
|
|
234
|
-
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
235
|
-
if (actualType !== expectedType && expectedType !== 'any') {
|
|
236
|
-
// Type mismatch - skip this field
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Recursively sanitize nested objects
|
|
241
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
242
|
-
validated[field] = sanitizeObject(value);
|
|
243
|
-
} else if (Array.isArray(value)) {
|
|
244
|
-
validated[field] = value.map((item) =>
|
|
245
|
-
typeof item === 'object' && item !== null ? sanitizeObject(item) : item
|
|
246
|
-
);
|
|
247
|
-
} else {
|
|
248
|
-
validated[field] = value;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return validated as T;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// No schema - return sanitized object
|
|
256
|
-
return sanitizeObject(parsed) as T;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Recursively sanitize an object by removing dangerous properties
|
|
261
|
-
*
|
|
262
|
-
* @param obj - Object to sanitize
|
|
263
|
-
* @returns Sanitized object
|
|
264
|
-
*/
|
|
265
|
-
function sanitizeObject(obj: any): any {
|
|
266
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
267
|
-
return obj;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const sanitized: Record<string, any> = {};
|
|
271
|
-
const dangerous = ['__proto__', 'constructor', 'prototype'];
|
|
272
|
-
|
|
273
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
274
|
-
// Skip dangerous keys
|
|
275
|
-
if (dangerous.includes(key)) {
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Recursively sanitize nested objects
|
|
280
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
281
|
-
sanitized[key] = sanitizeObject(value);
|
|
282
|
-
} else if (Array.isArray(value)) {
|
|
283
|
-
sanitized[key] = value.map((item) =>
|
|
284
|
-
typeof item === 'object' && item !== null ? sanitizeObject(item) : item
|
|
285
|
-
);
|
|
286
|
-
} else {
|
|
287
|
-
sanitized[key] = value;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return sanitized;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* LRU (Least Recently Used) cache with maximum size
|
|
296
|
-
* Used for preventing unbounded memory growth
|
|
297
|
-
*
|
|
298
|
-
* @template K - Key type
|
|
299
|
-
* @template V - Value type
|
|
300
|
-
*/
|
|
301
|
-
export class LRUCache<K, V> {
|
|
302
|
-
private cache = new Map<K, V>();
|
|
303
|
-
private readonly maxSize: number;
|
|
304
|
-
|
|
305
|
-
constructor(maxSize: number = 1000) {
|
|
306
|
-
if (maxSize <= 0) {
|
|
307
|
-
throw new Error('LRU cache maxSize must be positive');
|
|
308
|
-
}
|
|
309
|
-
this.maxSize = maxSize;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Get value from cache
|
|
314
|
-
*
|
|
315
|
-
* @param key - Cache key
|
|
316
|
-
* @returns Cached value or undefined
|
|
317
|
-
*/
|
|
318
|
-
get(key: K): V | undefined {
|
|
319
|
-
const value = this.cache.get(key);
|
|
320
|
-
if (value !== undefined) {
|
|
321
|
-
// Move to end (most recently used)
|
|
322
|
-
this.cache.delete(key);
|
|
323
|
-
this.cache.set(key, value);
|
|
324
|
-
}
|
|
325
|
-
return value;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Set value in cache
|
|
330
|
-
*
|
|
331
|
-
* @param key - Cache key
|
|
332
|
-
* @param value - Value to cache
|
|
333
|
-
*/
|
|
334
|
-
set(key: K, value: V): void {
|
|
335
|
-
// Delete if already exists (to update position)
|
|
336
|
-
if (this.cache.has(key)) {
|
|
337
|
-
this.cache.delete(key);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Evict oldest if at capacity
|
|
341
|
-
if (this.cache.size >= this.maxSize) {
|
|
342
|
-
const firstKey = this.cache.keys().next().value;
|
|
343
|
-
// TypeScript doesn't know that Map iterator always returns defined values when size > 0
|
|
344
|
-
// But we check size >= maxSize above, so this is safe
|
|
345
|
-
if (firstKey !== undefined) {
|
|
346
|
-
this.cache.delete(firstKey);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
this.cache.set(key, value);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Check if key exists in cache
|
|
355
|
-
*
|
|
356
|
-
* SECURITY FIX (N-1): Use Map's native has() instead of get()
|
|
357
|
-
* to avoid modifying LRU order on read-only operations.
|
|
358
|
-
*
|
|
359
|
-
* @param key - Cache key
|
|
360
|
-
* @returns true if key exists
|
|
361
|
-
*/
|
|
362
|
-
has(key: K): boolean {
|
|
363
|
-
return this.cache.has(key);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Delete key from cache
|
|
368
|
-
*
|
|
369
|
-
* @param key - Cache key
|
|
370
|
-
*/
|
|
371
|
-
delete(key: K): void {
|
|
372
|
-
this.cache.delete(key);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Clear all entries
|
|
377
|
-
*/
|
|
378
|
-
clear(): void {
|
|
379
|
-
this.cache.clear();
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Get current cache size
|
|
384
|
-
*/
|
|
385
|
-
get size(): number {
|
|
386
|
-
return this.cache.size;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Get all values from cache
|
|
391
|
-
*
|
|
392
|
-
* SECURITY FIX (N-2): Add iterator support for LRUCache.
|
|
393
|
-
* Returns values in LRU order (oldest to newest).
|
|
394
|
-
*
|
|
395
|
-
* @returns Array of all cached values
|
|
396
|
-
*/
|
|
397
|
-
values(): V[] {
|
|
398
|
-
return Array.from(this.cache.values());
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Get all keys from cache
|
|
403
|
-
*
|
|
404
|
-
* @returns Array of all cached keys
|
|
405
|
-
*/
|
|
406
|
-
keys(): K[] {
|
|
407
|
-
return Array.from(this.cache.keys());
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Get all entries from cache
|
|
412
|
-
*
|
|
413
|
-
* @returns Array of all cached [key, value] pairs
|
|
414
|
-
*/
|
|
415
|
-
entries(): [K, V][] {
|
|
416
|
-
return Array.from(this.cache.entries());
|
|
417
|
-
}
|
|
418
|
-
}
|