mastercontroller 1.3.24 → 1.3.26
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/MasterControl.js +22 -39
- package/MasterCors.js +1 -0
- package/MasterRouter.js +30 -6
- package/package.json +1 -1
package/MasterControl.js
CHANGED
|
@@ -146,25 +146,10 @@ class MasterControl {
|
|
|
146
146
|
// Only freeze in production to allow for easier debugging in development
|
|
147
147
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Object.freeze(Array.prototype);
|
|
154
|
-
Object.freeze(Function.prototype);
|
|
155
|
-
|
|
156
|
-
logger.info({
|
|
157
|
-
code: 'MC_SECURITY_PROTOTYPE_FROZEN',
|
|
158
|
-
message: 'Prototypes frozen in production mode for security'
|
|
159
|
-
});
|
|
160
|
-
} catch (err) {
|
|
161
|
-
logger.warn({
|
|
162
|
-
code: 'MC_SECURITY_FREEZE_FAILED',
|
|
163
|
-
message: 'Failed to freeze prototypes',
|
|
164
|
-
error: err.message
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
|
149
|
+
// NOTE: Prototype freezing was removed. Freezing Object.prototype/Array.prototype/
|
|
150
|
+
// Function.prototype breaks third-party libraries (e.g., long, mysql2) that define
|
|
151
|
+
// properties on their prototypes after framework init. Prototype pollution protection
|
|
152
|
+
// is handled via input validation in MasterValidator.js instead.
|
|
168
153
|
|
|
169
154
|
// Add prototype pollution detection utility
|
|
170
155
|
this._detectPrototypePollution = (obj) => {
|
|
@@ -490,7 +475,7 @@ class MasterControl {
|
|
|
490
475
|
if(type === "http"){
|
|
491
476
|
$that.serverProtocol = "http";
|
|
492
477
|
const server = http.createServer(async function(req, res) {
|
|
493
|
-
$that.serverRun(req, res);
|
|
478
|
+
await $that.serverRun(req, res);
|
|
494
479
|
});
|
|
495
480
|
// Set server immediately so config can access it
|
|
496
481
|
$that.server = server;
|
|
@@ -534,7 +519,7 @@ class MasterControl {
|
|
|
534
519
|
if(credentials.honorCipherOrder === undefined){ credentials.honorCipherOrder = true; }
|
|
535
520
|
if(!credentials.ALPNProtocols){ credentials.ALPNProtocols = ['h2', 'http/1.1']; }
|
|
536
521
|
const server = https.createServer(credentials, async function(req, res) {
|
|
537
|
-
$that.serverRun(req, res);
|
|
522
|
+
await $that.serverRun(req, res);
|
|
538
523
|
});
|
|
539
524
|
// Set server immediately so config can access it
|
|
540
525
|
$that.server = server;
|
|
@@ -1006,25 +991,23 @@ class MasterControl {
|
|
|
1006
991
|
const $that = this;
|
|
1007
992
|
console.log("path", `${req.method} ${req.url}`);
|
|
1008
993
|
|
|
1009
|
-
// Create request context for middleware pipeline
|
|
1010
|
-
const parsedUrl = url.parse(req.url);
|
|
1011
|
-
const pathname = parsedUrl.pathname;
|
|
1012
|
-
const ext = path.parse(pathname).ext;
|
|
1013
|
-
|
|
1014
|
-
const context = {
|
|
1015
|
-
request: req,
|
|
1016
|
-
response: res,
|
|
1017
|
-
requrl: url.parse(req.url, true),
|
|
1018
|
-
pathName: pathname.replace(/^\/|\/$/g, '').toLowerCase(),
|
|
1019
|
-
type: req.method.toLowerCase(),
|
|
1020
|
-
params: {},
|
|
1021
|
-
state: {}, // User-defined state shared across middleware
|
|
1022
|
-
master: $that, // Access to framework instance
|
|
1023
|
-
isStatic: ext !== '' // Is this a static file request?
|
|
1024
|
-
};
|
|
1025
|
-
|
|
1026
|
-
// Execute middleware pipeline
|
|
1027
994
|
try {
|
|
995
|
+
const parsedUrl = url.parse(req.url);
|
|
996
|
+
const pathname = parsedUrl.pathname;
|
|
997
|
+
const ext = path.parse(pathname).ext;
|
|
998
|
+
|
|
999
|
+
const context = {
|
|
1000
|
+
request: req,
|
|
1001
|
+
response: res,
|
|
1002
|
+
requrl: url.parse(req.url, true),
|
|
1003
|
+
pathName: pathname.replace(/^\/|\/$/g, '').toLowerCase(),
|
|
1004
|
+
type: req.method.toLowerCase(),
|
|
1005
|
+
params: {},
|
|
1006
|
+
state: {},
|
|
1007
|
+
master: $that,
|
|
1008
|
+
isStatic: ext !== ''
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1028
1011
|
await $that.pipeline.execute(context);
|
|
1029
1012
|
} catch (error) {
|
|
1030
1013
|
console.error('Pipeline execution failed:', error);
|
package/MasterCors.js
CHANGED
|
@@ -235,6 +235,7 @@ class MasterCors{
|
|
|
235
235
|
// Handle preflight OPTIONS request
|
|
236
236
|
if (ctx.type === 'options') {
|
|
237
237
|
$that.load({ request: ctx.request, response: ctx.response });
|
|
238
|
+
ctx.request.resume();
|
|
238
239
|
ctx.response.statusCode = 204;
|
|
239
240
|
ctx.response.end();
|
|
240
241
|
return; // Terminal - don't call next()
|
package/MasterRouter.js
CHANGED
|
@@ -805,11 +805,12 @@ class MasterRouter {
|
|
|
805
805
|
|
|
806
806
|
tools.combineObjectPrototype(Control, this._master.controllerList);
|
|
807
807
|
Control.prototype.__namespace = Control.name;
|
|
808
|
-
Control.prototype.__requestObject = requestObject;
|
|
809
|
-
Control.prototype.__currentRoute = currentRoute;
|
|
810
|
-
Control.prototype.__response = requestObject.response;
|
|
811
|
-
Control.prototype.__request = requestObject.request;
|
|
812
808
|
const control = new Control(requestObject);
|
|
809
|
+
// Set request-specific state on INSTANCE (not prototype) for concurrency safety
|
|
810
|
+
control.__requestObject = requestObject;
|
|
811
|
+
control.__currentRoute = currentRoute;
|
|
812
|
+
control.__response = requestObject.response;
|
|
813
|
+
control.__request = requestObject.request;
|
|
813
814
|
const _callEmit = new EventEmitter();
|
|
814
815
|
|
|
815
816
|
_callEmit.once(EVENT_NAMES.CONTROLLER, function(){
|
|
@@ -837,7 +838,16 @@ class MasterRouter {
|
|
|
837
838
|
// and no response was sent yet (e.g., overridden returnJson pattern)
|
|
838
839
|
if (returnValue !== undefined && returnValue !== null
|
|
839
840
|
&& !requestObject.response.headersSent && !requestObject.response._headerSent) {
|
|
840
|
-
const
|
|
841
|
+
const seen = new WeakSet();
|
|
842
|
+
const json = JSON.stringify(returnValue, (key, value) => {
|
|
843
|
+
if (typeof value === 'object' && value !== null) {
|
|
844
|
+
if (seen.has(value)) {
|
|
845
|
+
return '[Circular Reference]';
|
|
846
|
+
}
|
|
847
|
+
seen.add(value);
|
|
848
|
+
}
|
|
849
|
+
return value;
|
|
850
|
+
});
|
|
841
851
|
requestObject.response.writeHead(200, {
|
|
842
852
|
'Content-Type': 'application/json',
|
|
843
853
|
'Content-Length': Buffer.byteLength(json, 'utf8')
|
|
@@ -880,7 +890,21 @@ class MasterRouter {
|
|
|
880
890
|
|
|
881
891
|
// check if before function is avaliable and wait for it to return
|
|
882
892
|
if(control.__hasBeforeAction(control, requestObject)){
|
|
883
|
-
control.__callBeforeAction(control, requestObject, _callEmit)
|
|
893
|
+
control.__callBeforeAction(control, requestObject, _callEmit)
|
|
894
|
+
.catch((error) => {
|
|
895
|
+
if (!requestObject.response.headersSent && !requestObject.response._headerSent) {
|
|
896
|
+
const mcError = handleControllerError(
|
|
897
|
+
error,
|
|
898
|
+
requestObject.toController,
|
|
899
|
+
requestObject.toAction,
|
|
900
|
+
requestObject.pathName,
|
|
901
|
+
currentRoute.routeDef
|
|
902
|
+
);
|
|
903
|
+
sendErrorResponse(requestObject.response, mcError, requestObject.pathName);
|
|
904
|
+
}
|
|
905
|
+
performanceTracker.end(requestId);
|
|
906
|
+
_callEmit.removeAllListeners();
|
|
907
|
+
});
|
|
884
908
|
}else{
|
|
885
909
|
_callEmit.emit("controller");
|
|
886
910
|
}
|
package/package.json
CHANGED