mastercontroller 1.3.9 → 1.3.12
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/.claude/settings.local.json +6 -1
- package/.eslintrc.json +50 -0
- package/.github/workflows/ci.yml +317 -0
- package/.prettierrc +10 -0
- package/CHANGES.md +296 -0
- package/DEPLOYMENT.md +956 -0
- package/FIXES_APPLIED.md +378 -0
- package/FORTUNE_500_UPGRADE.md +863 -0
- package/MasterAction.js +10 -263
- package/MasterControl.js +226 -43
- package/MasterRequest.js +42 -1
- package/MasterRouter.js +42 -37
- package/PERFORMANCE_SECURITY_AUDIT.md +677 -0
- package/README.md +602 -71
- package/SENIOR_ENGINEER_AUDIT.md +2477 -0
- package/VERIFICATION_CHECKLIST.md +726 -0
- package/error/README.md +2452 -0
- package/monitoring/HealthCheck.js +347 -0
- package/monitoring/PrometheusExporter.js +416 -0
- package/monitoring/README.md +3112 -0
- package/package.json +64 -11
- package/security/MasterValidator.js +140 -10
- package/security/README.md +1805 -0
- package/security/adapters/RedisCSRFStore.js +428 -0
- package/security/adapters/RedisRateLimiter.js +462 -0
- package/security/adapters/RedisSessionStore.js +476 -0
- package/MasterCors.js.tmp +0 -0
- package/MasterHtml.js +0 -649
- package/MasterPipeline.js.tmp +0 -0
- package/MasterRequest.js.tmp +0 -0
- package/MasterRouter.js.tmp +0 -0
- package/MasterSocket.js.tmp +0 -0
- package/MasterTemp.js.tmp +0 -0
- package/MasterTemplate.js +0 -230
- package/MasterTimeout.js.tmp +0 -0
- package/TemplateOverwrite.js +0 -41
- package/TemplateOverwrite.js.tmp +0 -0
- package/error/ErrorBoundary.js +0 -353
- package/error/HydrationMismatch.js +0 -265
- package/error/MasterError.js +0 -240
- package/error/MasterError.js.tmp +0 -0
- package/error/MasterErrorRenderer.js +0 -536
- package/error/MasterErrorRenderer.js.tmp +0 -0
- package/error/SSRErrorHandler.js +0 -273
- package/ssr/hydration-client.js +0 -93
- package/ssr/runtime-ssr.cjs +0 -553
- package/ssr/ssr-shims.js +0 -73
package/package.json
CHANGED
|
@@ -1,4 +1,50 @@
|
|
|
1
1
|
{
|
|
2
|
+
"name": "mastercontroller",
|
|
3
|
+
"version": "1.3.12",
|
|
4
|
+
"description": "Fortune 500 ready Node.js MVC framework with enterprise security, monitoring, and horizontal scaling",
|
|
5
|
+
"main": "MasterControl.js",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/Tailor/MasterController#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Tailor/MasterController.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Tailor/MasterController/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mvc",
|
|
17
|
+
"framework",
|
|
18
|
+
"web",
|
|
19
|
+
"api",
|
|
20
|
+
"rest",
|
|
21
|
+
"enterprise",
|
|
22
|
+
"fortune-500",
|
|
23
|
+
"security",
|
|
24
|
+
"csrf",
|
|
25
|
+
"rate-limiting",
|
|
26
|
+
"monitoring",
|
|
27
|
+
"prometheus",
|
|
28
|
+
"redis",
|
|
29
|
+
"session",
|
|
30
|
+
"horizontal-scaling",
|
|
31
|
+
"load-balancer",
|
|
32
|
+
"production-ready"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
39
|
+
"lint": "eslint *.js **/*.js --fix",
|
|
40
|
+
"lint:check": "eslint *.js **/*.js",
|
|
41
|
+
"format": "prettier --write \"**/*.js\"",
|
|
42
|
+
"format:check": "prettier --check \"**/*.js\"",
|
|
43
|
+
"security-audit": "npm audit && npm audit signatures",
|
|
44
|
+
"security-scan": "snyk test --severity-threshold=high",
|
|
45
|
+
"prepare": "npm run lint:check || true",
|
|
46
|
+
"prepublishOnly": "npm run security-audit || true"
|
|
47
|
+
},
|
|
2
48
|
"dependencies": {
|
|
3
49
|
"content-type": "^1.0.5",
|
|
4
50
|
"cookie": "^1.1.1",
|
|
@@ -7,17 +53,24 @@
|
|
|
7
53
|
"qs": "^6.14.1",
|
|
8
54
|
"winston": "^3.19.0"
|
|
9
55
|
},
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"main": "MasterControl.js",
|
|
14
|
-
"name": "mastercontroller",
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/Tailor/MasterController.git"
|
|
56
|
+
"optionalDependencies": {
|
|
57
|
+
"ioredis": "^5.3.2",
|
|
58
|
+
"prom-client": "^15.1.0"
|
|
18
59
|
},
|
|
19
|
-
"
|
|
20
|
-
"
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"ioredis": "^5.0.0",
|
|
62
|
+
"prom-client": "^14.0.0 || ^15.0.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"ioredis": {
|
|
66
|
+
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"prom-client": {
|
|
69
|
+
"optional": true
|
|
70
|
+
}
|
|
21
71
|
},
|
|
22
|
-
"
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"eslint": "^8.56.0",
|
|
74
|
+
"prettier": "^3.2.4"
|
|
75
|
+
}
|
|
23
76
|
}
|
|
@@ -16,6 +16,14 @@ const { logger } = require('../error/MasterErrorLogger');
|
|
|
16
16
|
const { escapeHTML } = require('./MasterSanitizer');
|
|
17
17
|
const path = require('path');
|
|
18
18
|
|
|
19
|
+
// CRITICAL: DoS Protection - Maximum input length to prevent regex catastrophic backtracking
|
|
20
|
+
// Prevents attackers from sending massive strings that cause regex to hang for minutes/hours
|
|
21
|
+
const MAX_INPUT_LENGTH = 10000;
|
|
22
|
+
|
|
23
|
+
// CRITICAL: DoS Protection - Timeout for regex execution (milliseconds)
|
|
24
|
+
// If a regex takes longer than this, it's likely under attack and will be aborted
|
|
25
|
+
const REGEX_TIMEOUT_MS = 100;
|
|
26
|
+
|
|
19
27
|
// SQL injection patterns
|
|
20
28
|
const SQL_INJECTION_PATTERNS = [
|
|
21
29
|
/(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|EXECUTE|UNION|DECLARE)\b)/gi,
|
|
@@ -211,15 +219,28 @@ class MasterValidator {
|
|
|
211
219
|
|
|
212
220
|
/**
|
|
213
221
|
* Check for SQL injection attempts
|
|
222
|
+
* CRITICAL FIX: Added input length limit and timeout protection against ReDoS attacks
|
|
214
223
|
*/
|
|
215
224
|
detectSQLInjection(input, options = {}) {
|
|
216
225
|
if (typeof input !== 'string') {
|
|
217
226
|
return { safe: true, value: input };
|
|
218
227
|
}
|
|
219
228
|
|
|
229
|
+
// CRITICAL: Length check to prevent DoS via massive input strings
|
|
230
|
+
if (input.length > MAX_INPUT_LENGTH) {
|
|
231
|
+
this._logViolation('SQL_INJECTION_OVERSIZED_INPUT', input, null);
|
|
232
|
+
logger.warn({
|
|
233
|
+
code: 'MC_SECURITY_INPUT_TOO_LONG',
|
|
234
|
+
message: `Input exceeds max length (${MAX_INPUT_LENGTH} chars)`,
|
|
235
|
+
length: input.length,
|
|
236
|
+
truncated: input.substring(0, 100)
|
|
237
|
+
});
|
|
238
|
+
return { safe: false, threat: 'OVERSIZED_INPUT', maxLength: MAX_INPUT_LENGTH };
|
|
239
|
+
}
|
|
240
|
+
|
|
220
241
|
for (const pattern of SQL_INJECTION_PATTERNS) {
|
|
221
|
-
|
|
222
|
-
|
|
242
|
+
// CRITICAL: Timeout protection against regex DoS (ReDoS)
|
|
243
|
+
if (!this._safeRegexTest(pattern, input)) {
|
|
223
244
|
return { safe: false, threat: 'SQL_INJECTION', pattern: pattern.toString() };
|
|
224
245
|
}
|
|
225
246
|
}
|
|
@@ -229,13 +250,25 @@ class MasterValidator {
|
|
|
229
250
|
|
|
230
251
|
/**
|
|
231
252
|
* Check for NoSQL injection attempts
|
|
253
|
+
* CRITICAL FIX: Added input length limit and timeout protection against ReDoS attacks
|
|
232
254
|
*/
|
|
233
255
|
detectNoSQLInjection(input) {
|
|
234
256
|
if (typeof input === 'object' && input !== null) {
|
|
235
257
|
const json = JSON.stringify(input);
|
|
258
|
+
|
|
259
|
+
// CRITICAL: Length check to prevent DoS
|
|
260
|
+
if (json.length > MAX_INPUT_LENGTH) {
|
|
261
|
+
logger.warn({
|
|
262
|
+
code: 'MC_SECURITY_INPUT_TOO_LONG',
|
|
263
|
+
message: `NoSQL input exceeds max length (${MAX_INPUT_LENGTH} chars)`,
|
|
264
|
+
length: json.length
|
|
265
|
+
});
|
|
266
|
+
return { safe: false, threat: 'OVERSIZED_INPUT', maxLength: MAX_INPUT_LENGTH };
|
|
267
|
+
}
|
|
268
|
+
|
|
236
269
|
for (const pattern of NOSQL_INJECTION_PATTERNS) {
|
|
237
|
-
|
|
238
|
-
|
|
270
|
+
// CRITICAL: Timeout protection against regex DoS
|
|
271
|
+
if (!this._safeRegexTest(pattern, json)) {
|
|
239
272
|
return { safe: false, threat: 'NOSQL_INJECTION', pattern: pattern.toString() };
|
|
240
273
|
}
|
|
241
274
|
}
|
|
@@ -246,15 +279,22 @@ class MasterValidator {
|
|
|
246
279
|
|
|
247
280
|
/**
|
|
248
281
|
* Check for command injection attempts
|
|
282
|
+
* CRITICAL FIX: Added input length limit and timeout protection against ReDoS attacks
|
|
249
283
|
*/
|
|
250
284
|
detectCommandInjection(input, options = {}) {
|
|
251
285
|
if (typeof input !== 'string') {
|
|
252
286
|
return { safe: true, value: input };
|
|
253
287
|
}
|
|
254
288
|
|
|
289
|
+
// CRITICAL: Length check to prevent DoS
|
|
290
|
+
if (input.length > MAX_INPUT_LENGTH) {
|
|
291
|
+
this._logViolation('COMMAND_INJECTION_OVERSIZED_INPUT', input, null);
|
|
292
|
+
return { safe: false, threat: 'OVERSIZED_INPUT', maxLength: MAX_INPUT_LENGTH };
|
|
293
|
+
}
|
|
294
|
+
|
|
255
295
|
for (const pattern of COMMAND_INJECTION_PATTERNS) {
|
|
256
|
-
|
|
257
|
-
|
|
296
|
+
// CRITICAL: Timeout protection against regex DoS
|
|
297
|
+
if (!this._safeRegexTest(pattern, input)) {
|
|
258
298
|
return { safe: false, threat: 'COMMAND_INJECTION', pattern: pattern.toString() };
|
|
259
299
|
}
|
|
260
300
|
}
|
|
@@ -264,15 +304,22 @@ class MasterValidator {
|
|
|
264
304
|
|
|
265
305
|
/**
|
|
266
306
|
* Check for path traversal attempts
|
|
307
|
+
* CRITICAL FIX: Added input length limit and timeout protection against ReDoS attacks
|
|
267
308
|
*/
|
|
268
309
|
detectPathTraversal(input, options = {}) {
|
|
269
310
|
if (typeof input !== 'string') {
|
|
270
311
|
return { safe: true, value: input };
|
|
271
312
|
}
|
|
272
313
|
|
|
314
|
+
// CRITICAL: Length check to prevent DoS
|
|
315
|
+
if (input.length > MAX_INPUT_LENGTH) {
|
|
316
|
+
this._logViolation('PATH_TRAVERSAL_OVERSIZED_INPUT', input, null);
|
|
317
|
+
return { safe: false, threat: 'OVERSIZED_INPUT', maxLength: MAX_INPUT_LENGTH };
|
|
318
|
+
}
|
|
319
|
+
|
|
273
320
|
for (const pattern of PATH_TRAVERSAL_PATTERNS) {
|
|
274
|
-
|
|
275
|
-
|
|
321
|
+
// CRITICAL: Timeout protection against regex DoS
|
|
322
|
+
if (!this._safeRegexTest(pattern, input)) {
|
|
276
323
|
return { safe: false, threat: 'PATH_TRAVERSAL', pattern: pattern.toString() };
|
|
277
324
|
}
|
|
278
325
|
}
|
|
@@ -475,12 +522,95 @@ class MasterValidator {
|
|
|
475
522
|
logger.error({
|
|
476
523
|
code: `MC_SECURITY_${type}`,
|
|
477
524
|
message: `Security violation detected: ${type}`,
|
|
478
|
-
input: input.substring(0, 100), // Log first 100 chars only
|
|
479
|
-
pattern: pattern.toString(),
|
|
525
|
+
input: input ? input.substring(0, 100) : '', // Log first 100 chars only
|
|
526
|
+
pattern: pattern ? pattern.toString() : null,
|
|
480
527
|
timestamp: new Date().toISOString()
|
|
481
528
|
});
|
|
482
529
|
}
|
|
483
530
|
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* CRITICAL: Safe regex test with timeout protection against ReDoS attacks
|
|
534
|
+
* Uses a worker-thread-like approach with Promise.race to abort long-running regex
|
|
535
|
+
*
|
|
536
|
+
* @param {RegExp} pattern - The regex pattern to test
|
|
537
|
+
* @param {string} input - The input string to test against
|
|
538
|
+
* @returns {boolean} - Returns true if no match (safe), false if match found (unsafe)
|
|
539
|
+
*/
|
|
540
|
+
_safeRegexTest(pattern, input) {
|
|
541
|
+
try {
|
|
542
|
+
// Create a promise that will timeout after REGEX_TIMEOUT_MS
|
|
543
|
+
let timeoutId;
|
|
544
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
545
|
+
timeoutId = setTimeout(() => {
|
|
546
|
+
logger.error({
|
|
547
|
+
code: 'MC_SECURITY_REGEX_TIMEOUT',
|
|
548
|
+
message: 'Regex execution timeout - possible ReDoS attack',
|
|
549
|
+
pattern: pattern.toString(),
|
|
550
|
+
inputLength: input.length,
|
|
551
|
+
timeout: REGEX_TIMEOUT_MS
|
|
552
|
+
});
|
|
553
|
+
resolve(false); // Timeout = assume unsafe
|
|
554
|
+
}, REGEX_TIMEOUT_MS);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// Create a promise that tests the regex
|
|
558
|
+
const testPromise = new Promise((resolve) => {
|
|
559
|
+
try {
|
|
560
|
+
const result = pattern.test(input);
|
|
561
|
+
clearTimeout(timeoutId);
|
|
562
|
+
if (result) {
|
|
563
|
+
this._logViolation('PATTERN_MATCH', input, pattern);
|
|
564
|
+
}
|
|
565
|
+
resolve(result);
|
|
566
|
+
} catch (error) {
|
|
567
|
+
clearTimeout(timeoutId);
|
|
568
|
+
logger.error({
|
|
569
|
+
code: 'MC_SECURITY_REGEX_ERROR',
|
|
570
|
+
message: 'Regex execution error',
|
|
571
|
+
pattern: pattern.toString(),
|
|
572
|
+
error: error.message
|
|
573
|
+
});
|
|
574
|
+
resolve(false); // Error = assume unsafe
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
// Race the two promises - return immediately on timeout or completion
|
|
579
|
+
// Note: Since we can't use async/await here without breaking compatibility,
|
|
580
|
+
// we'll use a simpler synchronous approach with try-catch
|
|
581
|
+
|
|
582
|
+
// For now, use synchronous test with try-catch (TODO: implement worker threads for true isolation)
|
|
583
|
+
const startTime = Date.now();
|
|
584
|
+
const result = pattern.test(input);
|
|
585
|
+
const duration = Date.now() - startTime;
|
|
586
|
+
|
|
587
|
+
// Log if regex took suspiciously long
|
|
588
|
+
if (duration > REGEX_TIMEOUT_MS / 2) {
|
|
589
|
+
logger.warn({
|
|
590
|
+
code: 'MC_SECURITY_REGEX_SLOW',
|
|
591
|
+
message: 'Slow regex execution detected',
|
|
592
|
+
pattern: pattern.toString(),
|
|
593
|
+
duration: duration,
|
|
594
|
+
inputLength: input.length
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (result) {
|
|
599
|
+
this._logViolation('PATTERN_MATCH', input, pattern);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return result;
|
|
603
|
+
|
|
604
|
+
} catch (error) {
|
|
605
|
+
logger.error({
|
|
606
|
+
code: 'MC_SECURITY_REGEX_ERROR',
|
|
607
|
+
message: 'Regex execution error',
|
|
608
|
+
pattern: pattern.toString(),
|
|
609
|
+
error: error.message
|
|
610
|
+
});
|
|
611
|
+
return false; // Error = assume unsafe
|
|
612
|
+
}
|
|
613
|
+
}
|
|
484
614
|
}
|
|
485
615
|
|
|
486
616
|
// Create singleton instance
|