mastercontroller 1.2.12 → 1.2.13
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/CSPConfig.js +319 -0
- package/EventHandlerValidator.js +464 -0
- package/MasterAction.js +296 -72
- package/MasterBackendErrorHandler.js +769 -0
- package/MasterBenchmark.js +89 -0
- package/MasterBuildOptimizer.js +376 -0
- package/MasterBundleAnalyzer.js +108 -0
- package/MasterCache.js +400 -0
- package/MasterControl.js +76 -6
- package/MasterErrorHandler.js +487 -0
- package/MasterErrorLogger.js +360 -0
- package/MasterErrorMiddleware.js +407 -0
- package/MasterHtml.js +101 -14
- package/MasterMemoryMonitor.js +188 -0
- package/MasterProfiler.js +409 -0
- package/MasterRouter.js +273 -66
- package/MasterSanitizer.js +429 -0
- package/MasterTemplate.js +96 -3
- package/MasterValidator.js +546 -0
- package/README.md +0 -44
- package/SecurityMiddleware.js +486 -0
- package/SessionSecurity.js +416 -0
- package/package.json +3 -3
- package/ssr/ErrorBoundary.js +353 -0
- package/ssr/HTMLUtils.js +15 -0
- package/ssr/HydrationMismatch.js +265 -0
- package/ssr/PerformanceMonitor.js +233 -0
- package/ssr/SSRErrorHandler.js +273 -0
- package/ssr/hydration-client.js +93 -0
- package/ssr/runtime-ssr.cjs +553 -0
- package/ssr/ssr-shims.js +73 -0
- package/examples/FileServingExample.js +0 -88
package/MasterRouter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// version 0.0.
|
|
1
|
+
// version 0.0.249
|
|
2
2
|
|
|
3
3
|
var master = require('./MasterControl');
|
|
4
4
|
var toolClass = require('./MasterTools');
|
|
@@ -7,6 +7,17 @@ var path = require('path');
|
|
|
7
7
|
var currentRoute = {};
|
|
8
8
|
var tools = new toolClass();
|
|
9
9
|
|
|
10
|
+
// Enhanced error handling
|
|
11
|
+
const { handleRoutingError, handleControllerError, sendErrorResponse } = require('./MasterBackendErrorHandler');
|
|
12
|
+
const { logger } = require('./MasterErrorLogger');
|
|
13
|
+
const { performanceTracker, errorHandlerMiddleware } = require('./MasterErrorMiddleware');
|
|
14
|
+
|
|
15
|
+
// Security - Input validation and sanitization
|
|
16
|
+
const { validator, detectPathTraversal, detectSQLInjection, detectCommandInjection } = require('./MasterValidator');
|
|
17
|
+
const { escapeHTML } = require('./MasterSanitizer');
|
|
18
|
+
|
|
19
|
+
const isDevelopment = process.env.NODE_ENV !== 'production' && process.env.master === 'development';
|
|
20
|
+
|
|
10
21
|
var normalizePaths = function(requestPath, routePath, requestParams){
|
|
11
22
|
var obj = {
|
|
12
23
|
requestPath : "",
|
|
@@ -21,8 +32,14 @@ var tools = new toolClass();
|
|
|
21
32
|
routeItem = routePathList[i];
|
|
22
33
|
if(routeItem){
|
|
23
34
|
if(routeItem.indexOf(":") > -1){
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
const paramName = routeItem.replace(":", "");
|
|
36
|
+
const paramValue = requestItem;
|
|
37
|
+
|
|
38
|
+
// Security: Sanitize route parameter
|
|
39
|
+
const sanitizedValue = sanitizeRouteParam(paramName, paramValue);
|
|
40
|
+
|
|
41
|
+
requestParams[paramName] = sanitizedValue;
|
|
42
|
+
routePathList[i] = sanitizedValue;
|
|
26
43
|
}
|
|
27
44
|
}
|
|
28
45
|
}
|
|
@@ -32,10 +49,66 @@ var tools = new toolClass();
|
|
|
32
49
|
return obj;
|
|
33
50
|
}
|
|
34
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Sanitize route parameter to prevent injection attacks
|
|
54
|
+
*/
|
|
55
|
+
var sanitizeRouteParam = function(paramName, paramValue) {
|
|
56
|
+
if (!paramValue || typeof paramValue !== 'string') {
|
|
57
|
+
return paramValue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check for path traversal attempts
|
|
61
|
+
const pathCheck = detectPathTraversal(paramValue);
|
|
62
|
+
if (!pathCheck.safe) {
|
|
63
|
+
logger.warn({
|
|
64
|
+
code: 'MC_SECURITY_PATH_TRAVERSAL',
|
|
65
|
+
message: 'Path traversal attempt detected in route parameter',
|
|
66
|
+
param: paramName,
|
|
67
|
+
value: paramValue
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Remove dangerous content
|
|
71
|
+
return paramValue.replace(/\.\./g, '').replace(/\.\//g, '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check for SQL injection attempts
|
|
75
|
+
const sqlCheck = detectSQLInjection(paramValue);
|
|
76
|
+
if (!sqlCheck.safe) {
|
|
77
|
+
logger.warn({
|
|
78
|
+
code: 'MC_SECURITY_SQL_INJECTION',
|
|
79
|
+
message: 'SQL injection attempt detected in route parameter',
|
|
80
|
+
param: paramName,
|
|
81
|
+
value: paramValue
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Escape to prevent injection
|
|
85
|
+
return escapeHTML(paramValue);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check for command injection attempts
|
|
89
|
+
const cmdCheck = detectCommandInjection(paramValue);
|
|
90
|
+
if (!cmdCheck.safe) {
|
|
91
|
+
logger.warn({
|
|
92
|
+
code: 'MC_SECURITY_COMMAND_INJECTION',
|
|
93
|
+
message: 'Command injection attempt detected in route parameter',
|
|
94
|
+
param: paramName,
|
|
95
|
+
value: paramValue
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Remove dangerous characters
|
|
99
|
+
return paramValue.replace(/[;&|`$()]/g, '');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Basic sanitization for all params
|
|
103
|
+
return escapeHTML(paramValue);
|
|
104
|
+
}
|
|
105
|
+
|
|
35
106
|
var processRoutes = function(requestObject, emitter, routeObject){
|
|
36
107
|
var routeList = routeObject.routes;
|
|
37
108
|
var root = routeObject.root;
|
|
38
109
|
var isComponent = routeObject.isComponent;
|
|
110
|
+
var currentRouteBeingProcessed = null; // Track current route for better error messages
|
|
111
|
+
|
|
39
112
|
try{
|
|
40
113
|
// Ensure routes is an array
|
|
41
114
|
if(!Array.isArray(routeList)){
|
|
@@ -51,56 +124,109 @@ var tools = new toolClass();
|
|
|
51
124
|
if(routeList.length > 0){
|
|
52
125
|
// loop through routes
|
|
53
126
|
for(var item in routeList){
|
|
127
|
+
// Store current route for error handling
|
|
128
|
+
currentRouteBeingProcessed = {
|
|
129
|
+
path: routeList[item].path,
|
|
130
|
+
toController: routeList[item].toController,
|
|
131
|
+
toAction: routeList[item].toAction,
|
|
132
|
+
type: routeList[item].type
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
requestObject.toController = routeList[item].toController;
|
|
137
|
+
requestObject.toAction = routeList[item].toAction;
|
|
138
|
+
var pathObj = normalizePaths(requestObject.pathName, routeList[item].path, requestObject.params);
|
|
139
|
+
|
|
140
|
+
// if we find the route that matches the request
|
|
141
|
+
if(pathObj.requestPath === pathObj.routePath && routeList[item].type === requestObject.type){
|
|
142
|
+
|
|
143
|
+
// call Constraint
|
|
144
|
+
if(typeof routeList[item].constraint === "function"){
|
|
145
|
+
|
|
146
|
+
var newObj = {};
|
|
147
|
+
//tools.combineObjects(newObj, master.controllerList);
|
|
148
|
+
newObj.next = function(){
|
|
149
|
+
currentRoute.root = root;
|
|
150
|
+
currentRoute.pathName = requestObject.pathName;
|
|
151
|
+
currentRoute.toAction = requestObject.toAction;
|
|
152
|
+
currentRoute.toController = requestObject.toController;
|
|
153
|
+
currentRoute.response = requestObject.response;
|
|
154
|
+
currentRoute.isComponent = isComponent;
|
|
155
|
+
currentRoute.routeDef = currentRouteBeingProcessed; // Add route definition
|
|
156
|
+
emitter.emit("routeConstraintGood", requestObject);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Wrap constraint execution with error handling
|
|
160
|
+
try {
|
|
161
|
+
routeList[item].constraint.call(newObj, requestObject);
|
|
162
|
+
} catch(constraintError) {
|
|
163
|
+
const routeError = handleRoutingError(
|
|
164
|
+
requestObject.pathName,
|
|
165
|
+
[],
|
|
166
|
+
{
|
|
167
|
+
type: 'CONSTRAINT_ERROR',
|
|
168
|
+
route: currentRouteBeingProcessed,
|
|
169
|
+
error: constraintError
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
logger.error({
|
|
173
|
+
code: 'MC_ERR_ROUTE_CONSTRAINT',
|
|
174
|
+
message: `Route constraint failed for ${currentRouteBeingProcessed.path}`,
|
|
175
|
+
route: currentRouteBeingProcessed,
|
|
176
|
+
error: constraintError.message,
|
|
177
|
+
stack: constraintError.stack
|
|
178
|
+
});
|
|
179
|
+
throw constraintError;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return true;
|
|
183
|
+
}else{
|
|
54
184
|
|
|
55
|
-
requestObject.toController = routeList[item].toController;
|
|
56
|
-
requestObject.toAction = routeList[item].toAction;
|
|
57
|
-
var pathObj = normalizePaths(requestObject.pathName, routeList[item].path, requestObject.params);
|
|
58
|
-
// if we find the route that matches the request
|
|
59
|
-
if(pathObj.requestPath === pathObj.routePath && routeList[item].type === requestObject.type){
|
|
60
|
-
|
|
61
|
-
// call Constraint
|
|
62
|
-
if(typeof routeList[item].constraint === "function"){
|
|
63
|
-
|
|
64
|
-
var newObj = {};
|
|
65
|
-
//tools.combineObjects(newObj, master.controllerList);
|
|
66
|
-
newObj.next = function(){
|
|
67
185
|
currentRoute.root = root;
|
|
68
186
|
currentRoute.pathName = requestObject.pathName;
|
|
69
187
|
currentRoute.toAction = requestObject.toAction;
|
|
70
188
|
currentRoute.toController = requestObject.toController;
|
|
71
189
|
currentRoute.response = requestObject.response;
|
|
72
190
|
currentRoute.isComponent = isComponent;
|
|
191
|
+
currentRoute.routeDef = currentRouteBeingProcessed; // Add route definition
|
|
73
192
|
emitter.emit("routeConstraintGood", requestObject);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
currentRoute.response = requestObject.response;
|
|
84
|
-
currentRoute.isComponent = isComponent;
|
|
85
|
-
emitter.emit("routeConstraintGood", requestObject);
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if(pathObj.requestPath === pathObj.routePath && "options" ===requestObject.type.toLowerCase()){
|
|
199
|
+
// this means that the request is correct but its an options request means its the browser checking to see if the request is allowed
|
|
200
|
+
requestObject.response.writeHead(200, {'Content-Type': 'application/json'});
|
|
201
|
+
requestObject.response.end(JSON.stringify({"done": "true"}));
|
|
86
202
|
return true;
|
|
87
203
|
}
|
|
88
|
-
|
|
89
|
-
|
|
204
|
+
} catch(routeProcessError) {
|
|
205
|
+
// Log the specific route that failed
|
|
206
|
+
logger.error({
|
|
207
|
+
code: 'MC_ERR_ROUTE_PROCESS',
|
|
208
|
+
message: `Failed to process route: ${currentRouteBeingProcessed.path}`,
|
|
209
|
+
route: currentRouteBeingProcessed,
|
|
210
|
+
requestPath: requestObject.pathName,
|
|
211
|
+
error: routeProcessError.message,
|
|
212
|
+
stack: routeProcessError.stack
|
|
213
|
+
});
|
|
90
214
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
requestObject.response.writeHead(200, {'Content-Type': 'application/json'});
|
|
94
|
-
requestObject.response.end(JSON.stringify({"done": "true"}));
|
|
95
|
-
return true;
|
|
215
|
+
// Re-throw to be caught by outer try-catch
|
|
216
|
+
throw routeProcessError;
|
|
96
217
|
}
|
|
97
|
-
|
|
218
|
+
|
|
98
219
|
};
|
|
99
220
|
return -1;
|
|
100
221
|
}
|
|
101
222
|
}
|
|
102
223
|
catch(e){
|
|
103
|
-
|
|
224
|
+
// Enhanced error message with route context
|
|
225
|
+
const errorDetails = currentRouteBeingProcessed
|
|
226
|
+
? `\n\nFailing Route:\n Path: ${currentRouteBeingProcessed.path}\n Controller: ${currentRouteBeingProcessed.toController}#${currentRouteBeingProcessed.toAction}\n Method: ${currentRouteBeingProcessed.type.toUpperCase()}\n\nRequest:\n Path: ${requestObject.pathName}\n Method: ${requestObject.type.toUpperCase()}`
|
|
227
|
+
: '';
|
|
228
|
+
|
|
229
|
+
throw new Error(`Error processing routes: ${e.message}${errorDetails}\n\nOriginal Stack:\n${e.stack}`);
|
|
104
230
|
}
|
|
105
231
|
};
|
|
106
232
|
|
|
@@ -257,40 +383,113 @@ class MasterRouter {
|
|
|
257
383
|
|
|
258
384
|
_call(requestObject){
|
|
259
385
|
|
|
386
|
+
// Start performance tracking
|
|
387
|
+
const requestId = `${Date.now()}-${Math.random()}`;
|
|
388
|
+
performanceTracker.start(requestId, requestObject);
|
|
389
|
+
|
|
260
390
|
tools.combineObjects(master.requestList, requestObject);
|
|
261
391
|
requestObject = master.requestList;
|
|
262
392
|
var Control = null;
|
|
393
|
+
|
|
263
394
|
try{
|
|
264
|
-
|
|
265
|
-
}catch(e){
|
|
395
|
+
// Try to load controller
|
|
266
396
|
try{
|
|
267
|
-
Control = require(path.join(currentRoute.root, 'app', 'controllers', `${tools.
|
|
268
|
-
}catch(
|
|
269
|
-
|
|
397
|
+
Control = require(path.join(currentRoute.root, 'app', 'controllers', `${tools.firstLetterlowercase(requestObject.toController)}Controller`));
|
|
398
|
+
}catch(e){
|
|
399
|
+
try{
|
|
400
|
+
Control = require(path.join(currentRoute.root, 'app', 'controllers', `${tools.firstLetterUppercase(requestObject.toController)}Controller`));
|
|
401
|
+
}catch(e2){
|
|
402
|
+
// Controller not found - handle error
|
|
403
|
+
const error = handleControllerError(
|
|
404
|
+
new Error(`Controller not found: ${requestObject.toController}Controller`),
|
|
405
|
+
requestObject.toController,
|
|
406
|
+
requestObject.toAction,
|
|
407
|
+
requestObject.pathName,
|
|
408
|
+
currentRoute.routeDef // Pass route definition
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
sendErrorResponse(requestObject.response, error, requestObject.pathName);
|
|
412
|
+
performanceTracker.end(requestId);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
270
415
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
416
|
+
|
|
417
|
+
tools.combineObjectPrototype(Control, master.controllerList);
|
|
418
|
+
Control.prototype.__namespace = Control.name;
|
|
419
|
+
Control.prototype.__requestObject = requestObject;
|
|
420
|
+
Control.prototype.__currentRoute = currentRoute;
|
|
421
|
+
Control.prototype.__response = requestObject.response;
|
|
422
|
+
Control.prototype.__request = requestObject.request;
|
|
423
|
+
var control = new Control(requestObject);
|
|
424
|
+
var _callEmit = new EventEmitter();
|
|
425
|
+
|
|
426
|
+
_callEmit.on("controller", function(){
|
|
427
|
+
try {
|
|
428
|
+
control.next = function(){
|
|
429
|
+
control.__callAfterAction(control, requestObject);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Check if action exists
|
|
433
|
+
if (typeof control[requestObject.toAction] !== 'function') {
|
|
434
|
+
throw new Error(`Action '${requestObject.toAction}' not found in controller ${requestObject.toController}`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Wrap action with error handling
|
|
438
|
+
const wrappedAction = errorHandlerMiddleware(
|
|
439
|
+
control[requestObject.toAction],
|
|
440
|
+
requestObject.toController,
|
|
441
|
+
requestObject.toAction
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
// Execute action
|
|
445
|
+
Promise.resolve(wrappedAction.call(control, requestObject))
|
|
446
|
+
.then(() => {
|
|
447
|
+
performanceTracker.end(requestId);
|
|
448
|
+
})
|
|
449
|
+
.catch((error) => {
|
|
450
|
+
const mcError = handleControllerError(
|
|
451
|
+
error,
|
|
452
|
+
requestObject.toController,
|
|
453
|
+
requestObject.toAction,
|
|
454
|
+
requestObject.pathName,
|
|
455
|
+
currentRoute.routeDef // Pass route definition
|
|
456
|
+
);
|
|
457
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
458
|
+
performanceTracker.end(requestId);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
} catch (error) {
|
|
462
|
+
// Action execution error
|
|
463
|
+
const mcError = handleControllerError(
|
|
464
|
+
error,
|
|
465
|
+
requestObject.toController,
|
|
466
|
+
requestObject.toAction,
|
|
467
|
+
requestObject.pathName,
|
|
468
|
+
currentRoute.routeDef // Pass route definition
|
|
469
|
+
);
|
|
470
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
471
|
+
performanceTracker.end(requestId);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// check if before function is avaliable and wait for it to return
|
|
476
|
+
if(control.__hasBeforeAction(control, requestObject)){
|
|
477
|
+
control.__callBeforeAction(control, requestObject, _callEmit);
|
|
478
|
+
}else{
|
|
479
|
+
_callEmit.emit("controller");
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
} catch (error) {
|
|
483
|
+
// General error
|
|
484
|
+
const mcError = handleControllerError(
|
|
485
|
+
error,
|
|
486
|
+
requestObject.toController,
|
|
487
|
+
requestObject.toAction,
|
|
488
|
+
requestObject.pathName,
|
|
489
|
+
currentRoute.routeDef // Pass route definition
|
|
490
|
+
);
|
|
491
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
492
|
+
performanceTracker.end(requestId);
|
|
294
493
|
}
|
|
295
494
|
|
|
296
495
|
}
|
|
@@ -319,8 +518,16 @@ class MasterRouter {
|
|
|
319
518
|
}
|
|
320
519
|
|
|
321
520
|
if(routeFound === false){
|
|
322
|
-
|
|
323
|
-
|
|
521
|
+
// Enhanced 404 handling
|
|
522
|
+
const allRoutes = [];
|
|
523
|
+
for (const route of routes) {
|
|
524
|
+
if (this._routes[route] && this._routes[route].routes) {
|
|
525
|
+
allRoutes.push(...this._routes[route].routes);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const mcError = handleRoutingError(requestObject.pathName, allRoutes);
|
|
530
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
324
531
|
}
|
|
325
532
|
|
|
326
533
|
}
|