@sparkleideas/plugins 3.0.0-alpha.10

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 (80) hide show
  1. package/README.md +401 -0
  2. package/__tests__/collection-manager.test.ts +332 -0
  3. package/__tests__/dependency-graph.test.ts +434 -0
  4. package/__tests__/enhanced-plugin-registry.test.ts +488 -0
  5. package/__tests__/plugin-registry.test.ts +368 -0
  6. package/__tests__/ruvector-bridge.test.ts +2429 -0
  7. package/__tests__/ruvector-integration.test.ts +1602 -0
  8. package/__tests__/ruvector-migrations.test.ts +1099 -0
  9. package/__tests__/ruvector-quantization.test.ts +846 -0
  10. package/__tests__/ruvector-streaming.test.ts +1088 -0
  11. package/__tests__/sdk.test.ts +325 -0
  12. package/__tests__/security.test.ts +348 -0
  13. package/__tests__/utils/ruvector-test-utils.ts +860 -0
  14. package/examples/plugin-creator/index.ts +636 -0
  15. package/examples/plugin-creator/plugin-creator.test.ts +312 -0
  16. package/examples/ruvector/README.md +288 -0
  17. package/examples/ruvector/attention-patterns.ts +394 -0
  18. package/examples/ruvector/basic-usage.ts +288 -0
  19. package/examples/ruvector/docker-compose.yml +75 -0
  20. package/examples/ruvector/gnn-analysis.ts +501 -0
  21. package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
  22. package/examples/ruvector/init-db.sql +119 -0
  23. package/examples/ruvector/quantization.ts +680 -0
  24. package/examples/ruvector/self-learning.ts +447 -0
  25. package/examples/ruvector/semantic-search.ts +576 -0
  26. package/examples/ruvector/streaming-large-data.ts +507 -0
  27. package/examples/ruvector/transactions.ts +594 -0
  28. package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
  29. package/examples/ruvector-plugins/index.ts +79 -0
  30. package/examples/ruvector-plugins/intent-router.ts +354 -0
  31. package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
  32. package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
  33. package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
  34. package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
  35. package/examples/ruvector-plugins/shared/index.ts +20 -0
  36. package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
  37. package/examples/ruvector-plugins/sona-learning.ts +445 -0
  38. package/package.json +97 -0
  39. package/src/collections/collection-manager.ts +661 -0
  40. package/src/collections/index.ts +56 -0
  41. package/src/collections/official/index.ts +1040 -0
  42. package/src/core/base-plugin.ts +416 -0
  43. package/src/core/plugin-interface.ts +215 -0
  44. package/src/hooks/index.ts +685 -0
  45. package/src/index.ts +378 -0
  46. package/src/integrations/agentic-flow.ts +743 -0
  47. package/src/integrations/index.ts +88 -0
  48. package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
  49. package/src/integrations/ruvector/attention-advanced.ts +1040 -0
  50. package/src/integrations/ruvector/attention-executor.ts +782 -0
  51. package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
  52. package/src/integrations/ruvector/attention.ts +1063 -0
  53. package/src/integrations/ruvector/gnn.ts +3050 -0
  54. package/src/integrations/ruvector/hyperbolic.ts +1948 -0
  55. package/src/integrations/ruvector/index.ts +394 -0
  56. package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
  57. package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
  58. package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
  59. package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
  60. package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
  61. package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
  62. package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
  63. package/src/integrations/ruvector/migrations/index.ts +35 -0
  64. package/src/integrations/ruvector/migrations/migrations.ts +647 -0
  65. package/src/integrations/ruvector/quantization.ts +2036 -0
  66. package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
  67. package/src/integrations/ruvector/self-learning.ts +2376 -0
  68. package/src/integrations/ruvector/streaming.ts +1737 -0
  69. package/src/integrations/ruvector/types.ts +1945 -0
  70. package/src/providers/index.ts +643 -0
  71. package/src/registry/dependency-graph.ts +568 -0
  72. package/src/registry/enhanced-plugin-registry.ts +994 -0
  73. package/src/registry/plugin-registry.ts +604 -0
  74. package/src/sdk/index.ts +563 -0
  75. package/src/security/index.ts +594 -0
  76. package/src/types/index.ts +446 -0
  77. package/src/workers/index.ts +700 -0
  78. package/tmp.json +0 -0
  79. package/tsconfig.json +25 -0
  80. package/vitest.config.ts +23 -0
@@ -0,0 +1,594 @@
1
+ /**
2
+ * Security Module
3
+ *
4
+ * Provides security utilities for plugin development.
5
+ * Implements best practices for input validation, sanitization, and safe operations.
6
+ */
7
+
8
+ import * as path from 'path';
9
+ import * as fs from 'fs/promises';
10
+ import * as crypto from 'crypto';
11
+
12
+ // ============================================================================
13
+ // Input Validation
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Validate and sanitize a string input.
18
+ */
19
+ export function validateString(
20
+ input: unknown,
21
+ options?: {
22
+ minLength?: number;
23
+ maxLength?: number;
24
+ pattern?: RegExp;
25
+ trim?: boolean;
26
+ lowercase?: boolean;
27
+ uppercase?: boolean;
28
+ }
29
+ ): string | null {
30
+ if (typeof input !== 'string') return null;
31
+
32
+ let value = input;
33
+
34
+ if (options?.trim) value = value.trim();
35
+ if (options?.lowercase) value = value.toLowerCase();
36
+ if (options?.uppercase) value = value.toUpperCase();
37
+
38
+ if (options?.minLength !== undefined && value.length < options.minLength) return null;
39
+ if (options?.maxLength !== undefined && value.length > options.maxLength) return null;
40
+ if (options?.pattern && !options.pattern.test(value)) return null;
41
+
42
+ return value;
43
+ }
44
+
45
+ /**
46
+ * Validate a number input.
47
+ */
48
+ export function validateNumber(
49
+ input: unknown,
50
+ options?: {
51
+ min?: number;
52
+ max?: number;
53
+ integer?: boolean;
54
+ }
55
+ ): number | null {
56
+ const num = typeof input === 'number' ? input : parseFloat(String(input));
57
+
58
+ if (isNaN(num) || !isFinite(num)) return null;
59
+ if (options?.min !== undefined && num < options.min) return null;
60
+ if (options?.max !== undefined && num > options.max) return null;
61
+ if (options?.integer && !Number.isInteger(num)) return null;
62
+
63
+ return num;
64
+ }
65
+
66
+ /**
67
+ * Validate a boolean input.
68
+ */
69
+ export function validateBoolean(input: unknown): boolean | null {
70
+ if (typeof input === 'boolean') return input;
71
+ if (input === 'true' || input === '1' || input === 1) return true;
72
+ if (input === 'false' || input === '0' || input === 0) return false;
73
+ return null;
74
+ }
75
+
76
+ /**
77
+ * Validate an array input.
78
+ */
79
+ export function validateArray<T>(
80
+ input: unknown,
81
+ itemValidator: (item: unknown) => T | null,
82
+ options?: {
83
+ minLength?: number;
84
+ maxLength?: number;
85
+ unique?: boolean;
86
+ }
87
+ ): T[] | null {
88
+ if (!Array.isArray(input)) return null;
89
+
90
+ if (options?.minLength !== undefined && input.length < options.minLength) return null;
91
+ if (options?.maxLength !== undefined && input.length > options.maxLength) return null;
92
+
93
+ const result: T[] = [];
94
+ for (const item of input) {
95
+ const validated = itemValidator(item);
96
+ if (validated === null) return null;
97
+ result.push(validated);
98
+ }
99
+
100
+ if (options?.unique) {
101
+ const uniqueSet = new Set(result.map(String));
102
+ if (uniqueSet.size !== result.length) return null;
103
+ }
104
+
105
+ return result;
106
+ }
107
+
108
+ /**
109
+ * Validate an enum value.
110
+ */
111
+ export function validateEnum<T extends string>(
112
+ input: unknown,
113
+ allowedValues: readonly T[]
114
+ ): T | null {
115
+ if (typeof input !== 'string') return null;
116
+ if (!allowedValues.includes(input as T)) return null;
117
+ return input as T;
118
+ }
119
+
120
+ // ============================================================================
121
+ // Path Security
122
+ // ============================================================================
123
+
124
+ const MAX_PATH_LENGTH = 4096;
125
+ const BLOCKED_PATH_PATTERNS = [
126
+ /\.\./, // Parent directory traversal
127
+ /^~/, // Home directory expansion
128
+ /^\/etc\//i,
129
+ /^\/var\//i,
130
+ /^\/tmp\//i,
131
+ /^\/proc\//i,
132
+ /^\/sys\//i,
133
+ /^\/dev\//i,
134
+ /^C:\\Windows/i,
135
+ /^C:\\Program Files/i,
136
+ ];
137
+
138
+ /**
139
+ * Validate a file path for safety.
140
+ */
141
+ export function validatePath(
142
+ inputPath: unknown,
143
+ options?: {
144
+ allowedExtensions?: string[];
145
+ blockedPatterns?: RegExp[];
146
+ mustExist?: boolean;
147
+ allowAbsolute?: boolean;
148
+ }
149
+ ): string | null {
150
+ if (typeof inputPath !== 'string') return null;
151
+ if (inputPath.length === 0 || inputPath.length > MAX_PATH_LENGTH) return null;
152
+
153
+ // Normalize the path
154
+ const normalized = path.normalize(inputPath);
155
+
156
+ // Check blocked patterns
157
+ const blockedPatterns = [...BLOCKED_PATH_PATTERNS, ...(options?.blockedPatterns ?? [])];
158
+ for (const pattern of blockedPatterns) {
159
+ if (pattern.test(normalized)) return null;
160
+ }
161
+
162
+ // Check absolute path restriction
163
+ if (!options?.allowAbsolute && path.isAbsolute(normalized)) return null;
164
+
165
+ // Check extension
166
+ if (options?.allowedExtensions) {
167
+ const ext = path.extname(normalized).toLowerCase();
168
+ if (!options.allowedExtensions.includes(ext)) return null;
169
+ }
170
+
171
+ return normalized;
172
+ }
173
+
174
+ /**
175
+ * Create a safe path relative to a base directory.
176
+ * Prevents path traversal attacks.
177
+ */
178
+ export function safePath(baseDir: string, ...segments: string[]): string {
179
+ const resolved = path.resolve(baseDir, ...segments);
180
+ const normalizedBase = path.normalize(baseDir);
181
+
182
+ if (!resolved.startsWith(normalizedBase + path.sep) && resolved !== normalizedBase) {
183
+ throw new Error(`Path traversal blocked: ${resolved}`);
184
+ }
185
+
186
+ return resolved;
187
+ }
188
+
189
+ /**
190
+ * Async version of safePath that uses realpath.
191
+ * More secure as it resolves symlinks.
192
+ */
193
+ export async function safePathAsync(baseDir: string, ...segments: string[]): Promise<string> {
194
+ const resolved = path.resolve(baseDir, ...segments);
195
+
196
+ try {
197
+ const realResolved = await fs.realpath(resolved).catch(() => resolved);
198
+ const realBase = await fs.realpath(baseDir).catch(() => baseDir);
199
+
200
+ if (!realResolved.startsWith(realBase + path.sep) && realResolved !== realBase) {
201
+ throw new Error(`Path traversal blocked: ${realResolved}`);
202
+ }
203
+
204
+ return realResolved;
205
+ } catch (error) {
206
+ // Handle non-existent files
207
+ const normalizedBase = path.normalize(baseDir);
208
+ if (!resolved.startsWith(normalizedBase + path.sep) && resolved !== normalizedBase) {
209
+ throw new Error(`Path traversal blocked: ${resolved}`);
210
+ }
211
+ return resolved;
212
+ }
213
+ }
214
+
215
+ // ============================================================================
216
+ // JSON Security
217
+ // ============================================================================
218
+
219
+ const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
220
+
221
+ /**
222
+ * Parse JSON safely, stripping dangerous keys.
223
+ */
224
+ export function safeJsonParse<T = unknown>(content: string): T {
225
+ return JSON.parse(content, (key, value) => {
226
+ if (DANGEROUS_KEYS.has(key)) {
227
+ return undefined;
228
+ }
229
+ return value;
230
+ }) as T;
231
+ }
232
+
233
+ /**
234
+ * Stringify JSON with circular reference detection.
235
+ */
236
+ export function safeJsonStringify(
237
+ value: unknown,
238
+ options?: {
239
+ space?: number;
240
+ maxDepth?: number;
241
+ replacer?: (key: string, value: unknown) => unknown;
242
+ }
243
+ ): string {
244
+ const seen = new WeakSet();
245
+ const maxDepth = options?.maxDepth ?? 100;
246
+ let currentDepth = 0;
247
+
248
+ const replacer = (key: string, val: unknown): unknown => {
249
+ // Apply custom replacer first
250
+ if (options?.replacer) {
251
+ val = options.replacer(key, val);
252
+ }
253
+
254
+ // Strip dangerous keys
255
+ if (DANGEROUS_KEYS.has(key)) {
256
+ return undefined;
257
+ }
258
+
259
+ // Handle circular references
260
+ if (val !== null && typeof val === 'object') {
261
+ if (seen.has(val)) {
262
+ return '[Circular]';
263
+ }
264
+ seen.add(val);
265
+ }
266
+
267
+ // Depth limiting
268
+ if (key !== '') {
269
+ currentDepth++;
270
+ if (currentDepth > maxDepth) {
271
+ return '[Max Depth Exceeded]';
272
+ }
273
+ }
274
+
275
+ return val;
276
+ };
277
+
278
+ return JSON.stringify(value, replacer, options?.space);
279
+ }
280
+
281
+ // ============================================================================
282
+ // Command Security
283
+ // ============================================================================
284
+
285
+ const ALLOWED_COMMANDS = new Set([
286
+ 'npm', 'npx', 'node', 'git', 'tsc', 'vitest', 'jest',
287
+ 'prettier', 'eslint', 'ls', 'cat', 'grep', 'find',
288
+ ]);
289
+
290
+ const BLOCKED_COMMANDS = new Set([
291
+ 'rm', 'del', 'format', 'dd', 'mkfs', 'fdisk',
292
+ 'shutdown', 'reboot', 'poweroff', 'halt',
293
+ 'passwd', 'sudo', 'su', 'chmod', 'chown',
294
+ 'curl', 'wget', 'nc', 'netcat',
295
+ ]);
296
+
297
+ const SHELL_METACHARACTERS = /[|;&$`<>(){}[\]!\\]/;
298
+
299
+ /**
300
+ * Validate a command for safe execution.
301
+ */
302
+ export function validateCommand(
303
+ command: unknown,
304
+ options?: {
305
+ allowedCommands?: Set<string>;
306
+ blockedCommands?: Set<string>;
307
+ allowShellMetachars?: boolean;
308
+ }
309
+ ): { command: string; args: string[] } | null {
310
+ if (typeof command !== 'string') return null;
311
+
312
+ const trimmed = command.trim();
313
+ if (trimmed.length === 0) return null;
314
+
315
+ // Check for shell metacharacters
316
+ if (!options?.allowShellMetachars && SHELL_METACHARACTERS.test(trimmed)) {
317
+ return null;
318
+ }
319
+
320
+ // Parse command and args
321
+ const parts = trimmed.split(/\s+/);
322
+ const cmd = parts[0].toLowerCase();
323
+ const args = parts.slice(1);
324
+
325
+ // Check allowed/blocked lists
326
+ const allowed = options?.allowedCommands ?? ALLOWED_COMMANDS;
327
+ const blocked = options?.blockedCommands ?? BLOCKED_COMMANDS;
328
+
329
+ if (blocked.has(cmd)) return null;
330
+ if (!allowed.has(cmd) && allowed.size > 0) return null;
331
+
332
+ return { command: cmd, args };
333
+ }
334
+
335
+ /**
336
+ * Escape a string for safe shell argument use.
337
+ */
338
+ export function escapeShellArg(arg: string): string {
339
+ // Empty string
340
+ if (arg.length === 0) return "''";
341
+
342
+ // If no special characters, return as-is
343
+ if (!/[^a-zA-Z0-9_\-=./:@]/.test(arg)) return arg;
344
+
345
+ // Single-quote the argument and escape any single quotes
346
+ return "'" + arg.replace(/'/g, "'\"'\"'") + "'";
347
+ }
348
+
349
+ // ============================================================================
350
+ // Error Sanitization
351
+ // ============================================================================
352
+
353
+ const SENSITIVE_PATTERNS = [
354
+ /password[=:]\s*\S+/gi,
355
+ /api[_-]?key[=:]\s*\S+/gi,
356
+ /secret[=:]\s*\S+/gi,
357
+ /token[=:]\s*\S+/gi,
358
+ /auth[=:]\s*\S+/gi,
359
+ /bearer\s+\S+/gi,
360
+ /\/\/[^:]+:[^@]+@/g, // Credentials in URLs
361
+ ];
362
+
363
+ /**
364
+ * Sanitize error messages to remove sensitive data.
365
+ */
366
+ export function sanitizeErrorMessage(error: unknown): string {
367
+ const message = error instanceof Error ? error.message : String(error);
368
+
369
+ let sanitized = message;
370
+ for (const pattern of SENSITIVE_PATTERNS) {
371
+ sanitized = sanitized.replace(pattern, '[REDACTED]');
372
+ }
373
+
374
+ // Truncate very long messages
375
+ if (sanitized.length > 1000) {
376
+ sanitized = sanitized.substring(0, 1000) + '... [truncated]';
377
+ }
378
+
379
+ return sanitized;
380
+ }
381
+
382
+ /**
383
+ * Create a safe error object for logging/transmission.
384
+ */
385
+ export function sanitizeError(error: unknown): {
386
+ name: string;
387
+ message: string;
388
+ code?: string;
389
+ } {
390
+ if (error instanceof Error) {
391
+ return {
392
+ name: error.name,
393
+ message: sanitizeErrorMessage(error),
394
+ code: (error as NodeJS.ErrnoException).code,
395
+ };
396
+ }
397
+
398
+ return {
399
+ name: 'Error',
400
+ message: sanitizeErrorMessage(error),
401
+ };
402
+ }
403
+
404
+ // ============================================================================
405
+ // Rate Limiting
406
+ // ============================================================================
407
+
408
+ export interface RateLimiter {
409
+ tryAcquire(): boolean;
410
+ getRemaining(): number;
411
+ reset(): void;
412
+ }
413
+
414
+ /**
415
+ * Create a token bucket rate limiter.
416
+ */
417
+ export function createRateLimiter(options: {
418
+ maxTokens: number;
419
+ refillRate: number;
420
+ refillInterval: number;
421
+ }): RateLimiter {
422
+ let tokens = options.maxTokens;
423
+ let lastRefill = Date.now();
424
+
425
+ const refill = () => {
426
+ const now = Date.now();
427
+ const elapsed = now - lastRefill;
428
+ const refillCount = Math.floor(elapsed / options.refillInterval) * options.refillRate;
429
+
430
+ if (refillCount > 0) {
431
+ tokens = Math.min(options.maxTokens, tokens + refillCount);
432
+ lastRefill = now;
433
+ }
434
+ };
435
+
436
+ return {
437
+ tryAcquire(): boolean {
438
+ refill();
439
+ if (tokens > 0) {
440
+ tokens--;
441
+ return true;
442
+ }
443
+ return false;
444
+ },
445
+ getRemaining(): number {
446
+ refill();
447
+ return tokens;
448
+ },
449
+ reset(): void {
450
+ tokens = options.maxTokens;
451
+ lastRefill = Date.now();
452
+ },
453
+ };
454
+ }
455
+
456
+ // ============================================================================
457
+ // Crypto Utilities
458
+ // ============================================================================
459
+
460
+ /**
461
+ * Generate a secure random ID.
462
+ */
463
+ export function generateSecureId(length: number = 32): string {
464
+ return crypto.randomBytes(length).toString('hex');
465
+ }
466
+
467
+ /**
468
+ * Generate a secure random token (URL-safe).
469
+ */
470
+ export function generateSecureToken(length: number = 32): string {
471
+ return crypto.randomBytes(length).toString('base64url');
472
+ }
473
+
474
+ /**
475
+ * Hash a string securely.
476
+ */
477
+ export function hashString(input: string, algorithm: string = 'sha256'): string {
478
+ return crypto.createHash(algorithm).update(input).digest('hex');
479
+ }
480
+
481
+ /**
482
+ * Compare two strings in constant time.
483
+ */
484
+ export function constantTimeCompare(a: string, b: string): boolean {
485
+ if (a.length !== b.length) return false;
486
+
487
+ const bufA = Buffer.from(a);
488
+ const bufB = Buffer.from(b);
489
+
490
+ return crypto.timingSafeEqual(bufA, bufB);
491
+ }
492
+
493
+ // ============================================================================
494
+ // Resource Limits
495
+ // ============================================================================
496
+
497
+ export interface ResourceLimits {
498
+ maxMemoryMB: number;
499
+ maxCPUPercent: number;
500
+ maxFileSize: number;
501
+ maxOpenFiles: number;
502
+ maxExecutionTime: number;
503
+ }
504
+
505
+ const DEFAULT_LIMITS: ResourceLimits = {
506
+ maxMemoryMB: 512,
507
+ maxCPUPercent: 80,
508
+ maxFileSize: 10 * 1024 * 1024, // 10MB
509
+ maxOpenFiles: 100,
510
+ maxExecutionTime: 30000, // 30s
511
+ };
512
+
513
+ /**
514
+ * Create a resource limiter.
515
+ */
516
+ export function createResourceLimiter(limits?: Partial<ResourceLimits>): {
517
+ check(): { ok: boolean; violations: string[] };
518
+ enforce<T>(fn: () => Promise<T>): Promise<T>;
519
+ } {
520
+ const config = { ...DEFAULT_LIMITS, ...limits };
521
+
522
+ return {
523
+ check(): { ok: boolean; violations: string[] } {
524
+ const violations: string[] = [];
525
+ const memUsage = process.memoryUsage();
526
+ const memMB = memUsage.heapUsed / 1024 / 1024;
527
+
528
+ if (memMB > config.maxMemoryMB) {
529
+ violations.push(`Memory usage ${memMB.toFixed(1)}MB exceeds limit ${config.maxMemoryMB}MB`);
530
+ }
531
+
532
+ return {
533
+ ok: violations.length === 0,
534
+ violations,
535
+ };
536
+ },
537
+
538
+ async enforce<T>(fn: () => Promise<T>): Promise<T> {
539
+ const check = this.check();
540
+ if (!check.ok) {
541
+ throw new Error(`Resource limits exceeded: ${check.violations.join(', ')}`);
542
+ }
543
+
544
+ return Promise.race([
545
+ fn(),
546
+ new Promise<never>((_, reject) =>
547
+ setTimeout(() => reject(new Error('Execution time limit exceeded')), config.maxExecutionTime)
548
+ ),
549
+ ]);
550
+ },
551
+ };
552
+ }
553
+
554
+ // ============================================================================
555
+ // Export All
556
+ // ============================================================================
557
+
558
+ export const Security = {
559
+ // Validation
560
+ validateString,
561
+ validateNumber,
562
+ validateBoolean,
563
+ validateArray,
564
+ validateEnum,
565
+ validatePath,
566
+ validateCommand,
567
+
568
+ // Path security
569
+ safePath,
570
+ safePathAsync,
571
+
572
+ // JSON security
573
+ safeJsonParse,
574
+ safeJsonStringify,
575
+
576
+ // Command security
577
+ escapeShellArg,
578
+
579
+ // Error sanitization
580
+ sanitizeError,
581
+ sanitizeErrorMessage,
582
+
583
+ // Rate limiting
584
+ createRateLimiter,
585
+
586
+ // Crypto
587
+ generateSecureId,
588
+ generateSecureToken,
589
+ hashString,
590
+ constantTimeCompare,
591
+
592
+ // Resource limits
593
+ createResourceLimiter,
594
+ };