cipher-shield 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/LICENSE +21 -0
- package/README.md +666 -0
- package/index.js +75 -0
- package/package.json +57 -0
- package/src/core/aesEngine.js +369 -0
- package/src/core/blacklistMem.js +510 -0
- package/src/core/defconSystem.js +301 -0
- package/src/magicAuth.js +272 -0
- package/src/modules/aiScanner.js +482 -0
- package/src/modules/ghostHandler.js +279 -0
- package/src/modules/shadowHandler.js +266 -0
- package/src/shield.js +694 -0
- package/src/smartLogger.js +268 -0
- package/src/sslManager.js +345 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ghost Route Handler v2.1
|
|
3
|
+
*
|
|
4
|
+
* Advanced honeypot route detection system that identifies access to sensitive
|
|
5
|
+
* or trap endpoints. Implements multiple detection strategies for comprehensive
|
|
6
|
+
* coverage while maintaining high performance.
|
|
7
|
+
*
|
|
8
|
+
* Detection Strategies:
|
|
9
|
+
* - Exact path matching
|
|
10
|
+
* - Prefix matching for directory structures
|
|
11
|
+
* - File extension detection (e.g., .env, .git)
|
|
12
|
+
* - Pattern-based matching with wildcards
|
|
13
|
+
*
|
|
14
|
+
* Performance optimized with:
|
|
15
|
+
* - Pre-compiled regex patterns
|
|
16
|
+
* - Early termination on matches
|
|
17
|
+
* - Memory-efficient string operations
|
|
18
|
+
*
|
|
19
|
+
* @module ghostHandler
|
|
20
|
+
* @version 1.0.0
|
|
21
|
+
* @author Omindu Dissanayaka
|
|
22
|
+
* @license MIT
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Pre-compiled regex patterns for performance with security limits
|
|
27
|
+
* @private
|
|
28
|
+
* @type {Object.<string, RegExp>}
|
|
29
|
+
*/
|
|
30
|
+
const PATTERN_CACHE = new Map();
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Maximum pattern complexity to prevent ReDoS attacks
|
|
34
|
+
* @private
|
|
35
|
+
* @const {number}
|
|
36
|
+
*/
|
|
37
|
+
const MAX_PATTERN_LENGTH = 200;
|
|
38
|
+
const MAX_WILDCARD_COUNT = 3;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Compiles a ghost route pattern into an optimized regex with security checks
|
|
42
|
+
*
|
|
43
|
+
* @private
|
|
44
|
+
* @param {string} pattern - Ghost route pattern
|
|
45
|
+
* @returns {RegExp|null} Compiled regex for pattern matching or null if unsafe
|
|
46
|
+
*/
|
|
47
|
+
function compilePattern(pattern) {
|
|
48
|
+
if (PATTERN_CACHE.has(pattern)) {
|
|
49
|
+
return PATTERN_CACHE.get(pattern);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const wildcardCount = (pattern.match(/\*/g) || []).length;
|
|
57
|
+
if (wildcardCount > MAX_WILDCARD_COUNT) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
63
|
+
|
|
64
|
+
const regexPattern = escaped.replace(/\\\*/g, '[^/]*?');
|
|
65
|
+
|
|
66
|
+
const regex = new RegExp(`^${regexPattern}`, 'i');
|
|
67
|
+
|
|
68
|
+
if (!isSafeRegex(regex)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
PATTERN_CACHE.set(pattern, regex);
|
|
73
|
+
return regex;
|
|
74
|
+
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Tests regex for potential ReDoS vulnerabilities
|
|
82
|
+
*
|
|
83
|
+
* @private
|
|
84
|
+
* @param {RegExp} regex - Regex to test
|
|
85
|
+
* @returns {boolean} True if regex appears safe
|
|
86
|
+
*/
|
|
87
|
+
function isSafeRegex(regex) {
|
|
88
|
+
const testInputs = [
|
|
89
|
+
'a'.repeat(1000),
|
|
90
|
+
'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z',
|
|
91
|
+
'/admin/admin/admin/admin/admin/admin/admin/admin/admin',
|
|
92
|
+
'///admin///admin///admin///admin///admin'
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const input of testInputs) {
|
|
96
|
+
try {
|
|
97
|
+
const startTime = Date.now();
|
|
98
|
+
regex.test(input);
|
|
99
|
+
const executionTime = Date.now() - startTime;
|
|
100
|
+
|
|
101
|
+
if (executionTime > 100) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Detects if a request path matches any configured ghost route
|
|
114
|
+
*
|
|
115
|
+
* Implements multi-strategy detection:
|
|
116
|
+
* 1. Exact path matching (fastest)
|
|
117
|
+
* 2. Prefix matching for directory structures
|
|
118
|
+
* 3. File extension detection
|
|
119
|
+
* 4. Pattern matching with wildcards
|
|
120
|
+
*
|
|
121
|
+
* @function detect
|
|
122
|
+
* @param {string} requestPath - The request URL path to check
|
|
123
|
+
* @param {string[]} ghostRoutes - Array of ghost route patterns to match against
|
|
124
|
+
* @returns {boolean} True if the path matches any ghost route pattern
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```javascript
|
|
128
|
+
* const ghostRoutes = ['/admin', '.env', '/secret/*'];
|
|
129
|
+
* console.log(detect('/admin', ghostRoutes)); // true
|
|
130
|
+
* console.log(detect('/admin/login', ghostRoutes)); // true (prefix match)
|
|
131
|
+
* console.log(detect('/config/.env', ghostRoutes)); // true (extension match)
|
|
132
|
+
* console.log(detect('/secret/password', ghostRoutes)); // true (wildcard match)
|
|
133
|
+
* console.log(detect('/public', ghostRoutes)); // false
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
function detect(requestPath, ghostRoutes) {
|
|
137
|
+
if (!requestPath || typeof requestPath !== 'string') {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!Array.isArray(ghostRoutes) || ghostRoutes.length === 0) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const normalizedPath = requestPath.split('?')[0].toLowerCase().trim();
|
|
146
|
+
|
|
147
|
+
const cleanPath = normalizedPath.replace(/\/+$/, '') || '/';
|
|
148
|
+
|
|
149
|
+
for (const pattern of ghostRoutes) {
|
|
150
|
+
if (!pattern || typeof pattern !== 'string') {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const normalizedPattern = pattern.toLowerCase().trim();
|
|
155
|
+
|
|
156
|
+
if (cleanPath === normalizedPattern) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (cleanPath.startsWith(normalizedPattern + '/')) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (normalizedPattern.startsWith('.') && cleanPath.includes(normalizedPattern)) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (normalizedPattern.includes('*')) {
|
|
169
|
+
const regex = compilePattern(normalizedPattern);
|
|
170
|
+
if (regex && regex.test(cleanPath)) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (normalizedPattern.startsWith('/') && cleanPath.endsWith(normalizedPattern)) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Validates a ghost route pattern for correctness
|
|
185
|
+
*
|
|
186
|
+
* @function validatePattern
|
|
187
|
+
* @param {string} pattern - Pattern to validate
|
|
188
|
+
* @returns {boolean} True if pattern is valid
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```javascript
|
|
192
|
+
* console.log(validatePattern('/admin')); // true
|
|
193
|
+
* console.log(validatePattern('.env')); // true
|
|
194
|
+
* console.log(validatePattern('/secret/*')); // true
|
|
195
|
+
* console.log(validatePattern('')); // false
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
function validatePattern(pattern) {
|
|
199
|
+
if (!pattern || typeof pattern !== 'string') {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const trimmed = pattern.trim();
|
|
204
|
+
if (trimmed.length === 0) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (trimmed === '/' || trimmed === '/*') {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Gets statistics about ghost route patterns
|
|
217
|
+
*
|
|
218
|
+
* @function getPatternStats
|
|
219
|
+
* @param {string[]} ghostRoutes - Array of ghost route patterns
|
|
220
|
+
* @returns {Object} Statistics about the patterns
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```javascript
|
|
224
|
+
* const stats = getPatternStats(['/admin', '.env', '/secret/*']);
|
|
225
|
+
* console.log(stats); // { total: 3, exact: 1, extensions: 1, wildcards: 1 }
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
function getPatternStats(ghostRoutes) {
|
|
229
|
+
if (!Array.isArray(ghostRoutes)) {
|
|
230
|
+
return { total: 0, exact: 0, extensions: 0, wildcards: 0, invalid: 0 };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let exact = 0;
|
|
234
|
+
let extensions = 0;
|
|
235
|
+
let wildcards = 0;
|
|
236
|
+
let invalid = 0;
|
|
237
|
+
|
|
238
|
+
for (const pattern of ghostRoutes) {
|
|
239
|
+
if (!validatePattern(pattern)) {
|
|
240
|
+
invalid++;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (pattern.includes('*')) {
|
|
245
|
+
wildcards++;
|
|
246
|
+
} else if (pattern.startsWith('.')) {
|
|
247
|
+
extensions++;
|
|
248
|
+
} else {
|
|
249
|
+
exact++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
total: ghostRoutes.length,
|
|
255
|
+
exact,
|
|
256
|
+
extensions,
|
|
257
|
+
wildcards,
|
|
258
|
+
invalid
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Clears the pattern cache (useful for testing or memory management)
|
|
264
|
+
*
|
|
265
|
+
* @function clearCache
|
|
266
|
+
* @returns {number} Number of cached patterns cleared
|
|
267
|
+
*/
|
|
268
|
+
function clearCache() {
|
|
269
|
+
const size = PATTERN_CACHE.size;
|
|
270
|
+
PATTERN_CACHE.clear();
|
|
271
|
+
return size;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = {
|
|
275
|
+
detect,
|
|
276
|
+
validatePattern,
|
|
277
|
+
getPatternStats,
|
|
278
|
+
clearCache
|
|
279
|
+
};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow Ban Handler v2.1
|
|
3
|
+
*
|
|
4
|
+
* Intelligent time-wasting system for malicious IPs that delays responses
|
|
5
|
+
* without blocking the event loop or consuming excessive resources.
|
|
6
|
+
* Implements adaptive delays based on threat levels and DEFCON state.
|
|
7
|
+
*
|
|
8
|
+
* Key Features:
|
|
9
|
+
* - Non-blocking async delays that waste attacker time
|
|
10
|
+
* - Adaptive delay scaling based on DEFCON state
|
|
11
|
+
* - Configurable jitter to prevent timing analysis
|
|
12
|
+
* - Performance monitoring and statistics
|
|
13
|
+
* - Memory-efficient operation
|
|
14
|
+
*
|
|
15
|
+
* Shadow Ban vs Traditional Ban:
|
|
16
|
+
* - Doesn't immediately block (prevents attacker awareness)
|
|
17
|
+
* - Wastes attacker time with delays
|
|
18
|
+
* - Allows monitoring of attack patterns
|
|
19
|
+
* - Automatically expires via blacklist system
|
|
20
|
+
*
|
|
21
|
+
* @module shadowHandler
|
|
22
|
+
* @version 1.0.0
|
|
23
|
+
* @author Omindu Dissanayaka
|
|
24
|
+
* @license MIT
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Default configuration constants
|
|
29
|
+
* @readonly
|
|
30
|
+
*/
|
|
31
|
+
const DEFAULT_CONFIG = Object.freeze({
|
|
32
|
+
MIN_DELAY: 1000,
|
|
33
|
+
MAX_DELAY: 45000,
|
|
34
|
+
JITTER_PERCENT: 0.2,
|
|
35
|
+
RED_MULTIPLIER: 1.5,
|
|
36
|
+
YELLOW_MULTIPLIER: 1.2
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Performance tracking statistics
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
let stats = {
|
|
44
|
+
totalDelays: 0,
|
|
45
|
+
totalDelayTime: 0,
|
|
46
|
+
averageDelay: 0,
|
|
47
|
+
lastDelay: 0,
|
|
48
|
+
delaysByDefcon: {
|
|
49
|
+
GREEN: 0,
|
|
50
|
+
RED: 0
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Calculates adaptive delay based on DEFCON state and base delay
|
|
56
|
+
*
|
|
57
|
+
* @private
|
|
58
|
+
* @param {number} baseDelay - Base delay time in milliseconds
|
|
59
|
+
* @param {string} defconState - Current DEFCON state ('GREEN', 'RED', etc.)
|
|
60
|
+
* @returns {number} Calculated delay with DEFCON multiplier applied
|
|
61
|
+
*/
|
|
62
|
+
function calculateAdaptiveDelay(baseDelay, defconState) {
|
|
63
|
+
let multiplier = 1.0;
|
|
64
|
+
|
|
65
|
+
switch (defconState) {
|
|
66
|
+
case 'RED':
|
|
67
|
+
multiplier = DEFAULT_CONFIG.RED_MULTIPLIER;
|
|
68
|
+
break;
|
|
69
|
+
case 'YELLOW':
|
|
70
|
+
multiplier = DEFAULT_CONFIG.YELLOW_MULTIPLIER;
|
|
71
|
+
break;
|
|
72
|
+
case 'GREEN':
|
|
73
|
+
default:
|
|
74
|
+
multiplier = 1.0;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const adaptiveDelay = baseDelay * multiplier;
|
|
79
|
+
|
|
80
|
+
return Math.max(DEFAULT_CONFIG.MIN_DELAY,
|
|
81
|
+
Math.min(adaptiveDelay, DEFAULT_CONFIG.MAX_DELAY));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Applies random jitter to delay to prevent timing analysis attacks
|
|
86
|
+
* Uses cryptographically secure randomness for unpredictability
|
|
87
|
+
*
|
|
88
|
+
* @private
|
|
89
|
+
* @param {number} delay - Base delay time
|
|
90
|
+
* @returns {number} Delay with secure random jitter applied
|
|
91
|
+
*/
|
|
92
|
+
function applyJitter(delay) {
|
|
93
|
+
const jitterRange = delay * DEFAULT_CONFIG.JITTER_PERCENT;
|
|
94
|
+
const randomBytes = require('crypto').randomBytes(4);
|
|
95
|
+
const randomValue = randomBytes.readUInt32LE(0) / 0xFFFFFFFF;
|
|
96
|
+
|
|
97
|
+
const jitter = jitterRange * (randomValue * 2 - 1);
|
|
98
|
+
return Math.max(DEFAULT_CONFIG.MIN_DELAY, Math.floor(delay + jitter));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Delays request processing for blacklisted IPs using shadow ban technique
|
|
103
|
+
*
|
|
104
|
+
* This function implements the core shadow ban mechanism:
|
|
105
|
+
* 1. Calculates adaptive delay based on DEFCON state
|
|
106
|
+
* 2. Applies random jitter to prevent timing analysis
|
|
107
|
+
* 3. Performs non-blocking async delay
|
|
108
|
+
* 4. Updates performance statistics
|
|
109
|
+
* 5. Returns blocking decision
|
|
110
|
+
*
|
|
111
|
+
* The delay wastes attacker time while allowing legitimate monitoring
|
|
112
|
+
* of attack patterns and automated threat response.
|
|
113
|
+
*
|
|
114
|
+
* @function delay
|
|
115
|
+
* @param {string} ip - Client IP address being shadow banned
|
|
116
|
+
* @param {number} delayTime - Base delay duration in milliseconds
|
|
117
|
+
* @param {string} defconState - Current DEFCON state ('GREEN', 'RED', etc.)
|
|
118
|
+
* @returns {Promise<boolean>} Always returns true (indicating request should be blocked after delay)
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```javascript
|
|
122
|
+
* // Shadow ban an IP for 5 seconds in GREEN state
|
|
123
|
+
* const shouldBlock = await shadowHandler.delay('192.168.1.100', 5000, 'GREEN');
|
|
124
|
+
* if (shouldBlock) {
|
|
125
|
+
* res.status(408).json({ error: 'Request Timeout' });
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
async function delay(ip, delayTime, defconState = 'GREEN') {
|
|
130
|
+
const startTime = process.hrtime.bigint();
|
|
131
|
+
|
|
132
|
+
const baseDelay = Math.max(0, Math.min(delayTime || 0, DEFAULT_CONFIG.MAX_DELAY));
|
|
133
|
+
const cleanDefconState = (defconState || 'GREEN').toUpperCase();
|
|
134
|
+
|
|
135
|
+
const adaptiveDelay = calculateAdaptiveDelay(baseDelay, cleanDefconState);
|
|
136
|
+
|
|
137
|
+
const finalDelay = applyJitter(adaptiveDelay);
|
|
138
|
+
|
|
139
|
+
if (process.env.NODE_ENV === 'development') {
|
|
140
|
+
console.log(`[SHADOW] Delaying ${ip} for ${finalDelay}ms (${cleanDefconState} state)`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await new Promise(resolve => {
|
|
144
|
+
setTimeout(resolve, finalDelay);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const delayDuration = Number(process.hrtime.bigint() - startTime) / 1000000;
|
|
148
|
+
stats.totalDelays++;
|
|
149
|
+
stats.totalDelayTime += delayDuration;
|
|
150
|
+
stats.averageDelay = stats.totalDelayTime / stats.totalDelays;
|
|
151
|
+
stats.lastDelay = delayDuration;
|
|
152
|
+
stats.delaysByDefcon[cleanDefconState] = (stats.delaysByDefcon[cleanDefconState] || 0) + 1;
|
|
153
|
+
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Calculates random jitter for delay unpredictability (legacy function)
|
|
159
|
+
*
|
|
160
|
+
* @deprecated Use applyJitter() internally. This function is kept for backward compatibility.
|
|
161
|
+
* @function addJitter
|
|
162
|
+
* @param {number} baseDelay - Base delay time in milliseconds
|
|
163
|
+
* @returns {number} Delay with jitter applied
|
|
164
|
+
*/
|
|
165
|
+
function addJitter(baseDelay) {
|
|
166
|
+
return applyJitter(baseDelay);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Retrieves shadow ban performance statistics
|
|
171
|
+
*
|
|
172
|
+
* @function getStats
|
|
173
|
+
* @returns {Object} Performance statistics
|
|
174
|
+
* @property {number} totalDelays - Total number of delays performed
|
|
175
|
+
* @property {number} totalDelayTime - Total time spent delaying (ms)
|
|
176
|
+
* @property {number} averageDelay - Average delay duration (ms)
|
|
177
|
+
* @property {number} lastDelay - Most recent delay duration (ms)
|
|
178
|
+
* @property {Object} delaysByDefcon - Delays grouped by DEFCON state
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```javascript
|
|
182
|
+
* const stats = shadowHandler.getStats();
|
|
183
|
+
* console.log(`Average delay: ${stats.averageDelay}ms`);
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
function getStats() {
|
|
187
|
+
return {
|
|
188
|
+
...stats,
|
|
189
|
+
config: DEFAULT_CONFIG
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Resets performance statistics (useful for testing)
|
|
195
|
+
*
|
|
196
|
+
* @function resetStats
|
|
197
|
+
* @returns {Object} Previous statistics
|
|
198
|
+
*/
|
|
199
|
+
function resetStats() {
|
|
200
|
+
const previousStats = { ...stats };
|
|
201
|
+
stats = {
|
|
202
|
+
totalDelays: 0,
|
|
203
|
+
totalDelayTime: 0,
|
|
204
|
+
averageDelay: 0,
|
|
205
|
+
lastDelay: 0,
|
|
206
|
+
delaysByDefcon: {
|
|
207
|
+
GREEN: 0,
|
|
208
|
+
RED: 0
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
return previousStats;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Validates delay parameters for security and performance
|
|
216
|
+
*
|
|
217
|
+
* @function validateDelayParams
|
|
218
|
+
* @param {number} delayTime - Delay time to validate
|
|
219
|
+
* @param {string} defconState - DEFCON state to validate
|
|
220
|
+
* @returns {Object} Validation result
|
|
221
|
+
* @property {boolean} valid - Whether parameters are valid
|
|
222
|
+
* @property {string[]} errors - Array of validation errors
|
|
223
|
+
* @property {Object} sanitized - Sanitized parameters
|
|
224
|
+
*/
|
|
225
|
+
function validateDelayParams(delayTime, defconState) {
|
|
226
|
+
const errors = [];
|
|
227
|
+
const sanitized = {
|
|
228
|
+
delayTime: DEFAULT_CONFIG.MIN_DELAY,
|
|
229
|
+
defconState: 'GREEN'
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
if (typeof delayTime !== 'number' || isNaN(delayTime)) {
|
|
233
|
+
errors.push('Delay time must be a valid number');
|
|
234
|
+
} else if (delayTime < 0) {
|
|
235
|
+
errors.push('Delay time cannot be negative');
|
|
236
|
+
sanitized.delayTime = DEFAULT_CONFIG.MIN_DELAY;
|
|
237
|
+
} else if (delayTime > DEFAULT_CONFIG.MAX_DELAY) {
|
|
238
|
+
errors.push(`Delay time cannot exceed ${DEFAULT_CONFIG.MAX_DELAY}ms`);
|
|
239
|
+
sanitized.delayTime = DEFAULT_CONFIG.MAX_DELAY;
|
|
240
|
+
} else {
|
|
241
|
+
sanitized.delayTime = delayTime;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const validStates = ['GREEN', 'YELLOW', 'RED'];
|
|
245
|
+
const upperState = (defconState || '').toUpperCase();
|
|
246
|
+
if (!validStates.includes(upperState)) {
|
|
247
|
+
errors.push(`Invalid DEFCON state: ${defconState}. Must be one of: ${validStates.join(', ')}`);
|
|
248
|
+
} else {
|
|
249
|
+
sanitized.defconState = upperState;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
valid: errors.length === 0,
|
|
254
|
+
errors,
|
|
255
|
+
sanitized
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module.exports = {
|
|
260
|
+
delay,
|
|
261
|
+
addJitter,
|
|
262
|
+
getStats,
|
|
263
|
+
resetStats,
|
|
264
|
+
validateDelayParams,
|
|
265
|
+
DEFAULT_CONFIG
|
|
266
|
+
};
|