mastercontroller 1.3.35 → 1.3.36
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 +4 -4
- package/MasterCors.js +1 -1
- package/MasterPipeline.js +2 -2
- package/MasterTimeout.js +2 -2
- package/monitoring/HealthCheck.js +4 -2
- package/monitoring/PrometheusExporter.js +1 -1
- package/package.json +1 -1
- package/security/CSPConfig.js +3 -2
- package/security/SecurityEnforcement.js +1 -1
- package/security/SecurityMiddleware.js +18 -13
- package/security/SessionSecurity.js +3 -1
- package/security/adapters/RedisCSRFStore.js +5 -3
- package/security/adapters/RedisRateLimiter.js +2 -2
package/MasterControl.js
CHANGED
|
@@ -902,7 +902,7 @@ class MasterControl {
|
|
|
902
902
|
return; // Terminal - don't call next()
|
|
903
903
|
}
|
|
904
904
|
|
|
905
|
-
await next(); // Not static, continue pipeline
|
|
905
|
+
if (typeof next === 'function') await next(); // Not static, continue pipeline
|
|
906
906
|
});
|
|
907
907
|
|
|
908
908
|
// 2. Timeout Tracking (optional - disabled by default until init)
|
|
@@ -924,7 +924,7 @@ class MasterControl {
|
|
|
924
924
|
ctx.params.formData = params.formData;
|
|
925
925
|
}
|
|
926
926
|
|
|
927
|
-
await next();
|
|
927
|
+
if (typeof next === 'function') await next();
|
|
928
928
|
});
|
|
929
929
|
|
|
930
930
|
// 4. Load Scoped Services (per request - always needed)
|
|
@@ -938,7 +938,7 @@ class MasterControl {
|
|
|
938
938
|
const className = $that._scopedList[key];
|
|
939
939
|
$that.requestList[key] = new className();
|
|
940
940
|
}
|
|
941
|
-
await next();
|
|
941
|
+
if (typeof next === 'function') await next();
|
|
942
942
|
});
|
|
943
943
|
|
|
944
944
|
// 4. HSTS Header (if enabled for HTTPS)
|
|
@@ -954,7 +954,7 @@ class MasterControl {
|
|
|
954
954
|
}
|
|
955
955
|
ctx.response.setHeader('Strict-Transport-Security', hstsValue);
|
|
956
956
|
}
|
|
957
|
-
await next();
|
|
957
|
+
if (typeof next === 'function') await next();
|
|
958
958
|
});
|
|
959
959
|
|
|
960
960
|
// 5. Routing and Error Handler are registered in start() so that user
|
package/MasterCors.js
CHANGED
package/MasterPipeline.js
CHANGED
|
@@ -116,12 +116,12 @@ class MasterPipeline {
|
|
|
116
116
|
// Execute branch pipeline
|
|
117
117
|
await branch.execute(ctx);
|
|
118
118
|
// Stop if response already sent (e.g., auth rejection)
|
|
119
|
-
if (!ctx.response.headersSent && !ctx.response.writableEnded) {
|
|
119
|
+
if (!ctx.response.headersSent && !ctx.response.writableEnded && typeof next === 'function') {
|
|
120
120
|
await next();
|
|
121
121
|
}
|
|
122
122
|
} else {
|
|
123
123
|
// Skip branch, continue main pipeline
|
|
124
|
-
await next();
|
|
124
|
+
if (typeof next === 'function') await next();
|
|
125
125
|
}
|
|
126
126
|
};
|
|
127
127
|
|
package/MasterTimeout.js
CHANGED
|
@@ -483,7 +483,7 @@ class MasterTimeout {
|
|
|
483
483
|
|
|
484
484
|
return async (ctx, next) => {
|
|
485
485
|
if (!$that.enabled) {
|
|
486
|
-
await next();
|
|
486
|
+
if (typeof next === 'function') await next();
|
|
487
487
|
return;
|
|
488
488
|
}
|
|
489
489
|
|
|
@@ -493,7 +493,7 @@ class MasterTimeout {
|
|
|
493
493
|
requestId = $that.startTracking(ctx);
|
|
494
494
|
ctx.requestId = requestId;
|
|
495
495
|
|
|
496
|
-
await next();
|
|
496
|
+
if (typeof next === 'function') await next();
|
|
497
497
|
} catch (err) {
|
|
498
498
|
// Stop tracking on error (with error handling)
|
|
499
499
|
if (requestId) {
|
|
@@ -55,7 +55,8 @@ class HealthCheck {
|
|
|
55
55
|
|
|
56
56
|
// Only handle health check endpoint
|
|
57
57
|
if (requestPath !== self.options.endpoint) {
|
|
58
|
-
return await next();
|
|
58
|
+
if (typeof next === 'function') return await next();
|
|
59
|
+
return;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
// Log health check request
|
|
@@ -226,7 +227,8 @@ class HealthCheck {
|
|
|
226
227
|
const requestPath = req.url.split('?')[0];
|
|
227
228
|
|
|
228
229
|
if (requestPath !== self.options.endpoint) {
|
|
229
|
-
return next();
|
|
230
|
+
if (typeof next === 'function') return next();
|
|
231
|
+
return;
|
|
230
232
|
}
|
|
231
233
|
|
|
232
234
|
try {
|
package/package.json
CHANGED
package/security/CSPConfig.js
CHANGED
|
@@ -95,7 +95,8 @@ class CSPConfig {
|
|
|
95
95
|
middleware() {
|
|
96
96
|
return (req, res, next) => {
|
|
97
97
|
if (!this.enabled) {
|
|
98
|
-
|
|
98
|
+
if (typeof next === 'function') next();
|
|
99
|
+
return;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
// Generate nonce for this request if needed
|
|
@@ -111,7 +112,7 @@ class CSPConfig {
|
|
|
111
112
|
const headerName = this.reportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy';
|
|
112
113
|
res.setHeader(headerName, headerValue);
|
|
113
114
|
|
|
114
|
-
next();
|
|
115
|
+
if (typeof next === 'function') next();
|
|
115
116
|
};
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -74,7 +74,8 @@ class SecurityMiddleware {
|
|
|
74
74
|
*/
|
|
75
75
|
securityHeadersMiddleware(req, res, next) {
|
|
76
76
|
if (!this.headersEnabled) {
|
|
77
|
-
|
|
77
|
+
if (typeof next === 'function') next();
|
|
78
|
+
return;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
// Apply standard security headers
|
|
@@ -96,7 +97,7 @@ class SecurityMiddleware {
|
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
next();
|
|
100
|
+
if (typeof next === 'function') next();
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
/**
|
|
@@ -104,7 +105,8 @@ class SecurityMiddleware {
|
|
|
104
105
|
*/
|
|
105
106
|
corsMiddleware(req, res, next) {
|
|
106
107
|
if (!this.corsEnabled) {
|
|
107
|
-
|
|
108
|
+
if (typeof next === 'function') next();
|
|
109
|
+
return;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
const origin = req.headers.origin;
|
|
@@ -125,7 +127,7 @@ class SecurityMiddleware {
|
|
|
125
127
|
return;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
next();
|
|
130
|
+
if (typeof next === 'function') next();
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
/**
|
|
@@ -133,7 +135,8 @@ class SecurityMiddleware {
|
|
|
133
135
|
*/
|
|
134
136
|
rateLimitMiddleware(req, res, next) {
|
|
135
137
|
if (!this.rateLimitEnabled) {
|
|
136
|
-
|
|
138
|
+
if (typeof next === 'function') next();
|
|
139
|
+
return;
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
const identifier = this._getClientIdentifier(req);
|
|
@@ -209,7 +212,7 @@ class SecurityMiddleware {
|
|
|
209
212
|
res.setHeader('X-RateLimit-Remaining', remaining);
|
|
210
213
|
res.setHeader('X-RateLimit-Reset', new Date(resetTime).toISOString());
|
|
211
214
|
|
|
212
|
-
next();
|
|
215
|
+
if (typeof next === 'function') next();
|
|
213
216
|
}
|
|
214
217
|
|
|
215
218
|
/**
|
|
@@ -217,13 +220,15 @@ class SecurityMiddleware {
|
|
|
217
220
|
*/
|
|
218
221
|
csrfMiddleware(req, res, next) {
|
|
219
222
|
if (!this.csrfEnabled) {
|
|
220
|
-
|
|
223
|
+
if (typeof next === 'function') next();
|
|
224
|
+
return;
|
|
221
225
|
}
|
|
222
226
|
|
|
223
227
|
// Skip CSRF for safe methods
|
|
224
228
|
const safeMethods = ['GET', 'HEAD', 'OPTIONS'];
|
|
225
229
|
if (safeMethods.includes(req.method)) {
|
|
226
|
-
|
|
230
|
+
if (typeof next === 'function') next();
|
|
231
|
+
return;
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
// Get CSRF token from request
|
|
@@ -291,7 +296,7 @@ class SecurityMiddleware {
|
|
|
291
296
|
}
|
|
292
297
|
|
|
293
298
|
// Token valid, continue
|
|
294
|
-
next();
|
|
299
|
+
if (typeof next === 'function') next();
|
|
295
300
|
}
|
|
296
301
|
|
|
297
302
|
/**
|
|
@@ -489,7 +494,7 @@ function pipelineSecurityHeaders(options = {}) {
|
|
|
489
494
|
instance.securityHeadersMiddleware(ctx.request, ctx.response, oldNext);
|
|
490
495
|
|
|
491
496
|
// Continue pipeline if next was called
|
|
492
|
-
if (nextCalled) {
|
|
497
|
+
if (nextCalled && typeof next === 'function') {
|
|
493
498
|
await next();
|
|
494
499
|
}
|
|
495
500
|
};
|
|
@@ -504,7 +509,7 @@ function pipelineCors(options = {}) {
|
|
|
504
509
|
instance.corsMiddleware(ctx.request, ctx.response, oldNext);
|
|
505
510
|
|
|
506
511
|
// CORS might terminate for OPTIONS - check if response ended
|
|
507
|
-
if (!ctx.response.writableEnded && nextCalled) {
|
|
512
|
+
if (!ctx.response.writableEnded && nextCalled && typeof next === 'function') {
|
|
508
513
|
await next();
|
|
509
514
|
}
|
|
510
515
|
};
|
|
@@ -519,7 +524,7 @@ function pipelineRateLimit(options = {}) {
|
|
|
519
524
|
instance.rateLimitMiddleware(ctx.request, ctx.response, oldNext);
|
|
520
525
|
|
|
521
526
|
// Rate limit might terminate - check if response ended
|
|
522
|
-
if (!ctx.response.writableEnded && nextCalled) {
|
|
527
|
+
if (!ctx.response.writableEnded && nextCalled && typeof next === 'function') {
|
|
523
528
|
await next();
|
|
524
529
|
}
|
|
525
530
|
};
|
|
@@ -534,7 +539,7 @@ function pipelineCsrf(options = {}) {
|
|
|
534
539
|
instance.csrfMiddleware(ctx.request, ctx.response, oldNext);
|
|
535
540
|
|
|
536
541
|
// CSRF might terminate - check if response ended
|
|
537
|
-
if (!ctx.response.writableEnded && nextCalled) {
|
|
542
|
+
if (!ctx.response.writableEnded && nextCalled && typeof next === 'function') {
|
|
538
543
|
await next();
|
|
539
544
|
}
|
|
540
545
|
};
|
|
@@ -318,7 +318,8 @@ class RedisCSRFStore {
|
|
|
318
318
|
message: 'CSRF check skipped - no session ID',
|
|
319
319
|
path: ctx.request.url
|
|
320
320
|
});
|
|
321
|
-
return await next();
|
|
321
|
+
if (typeof next === 'function') return await next();
|
|
322
|
+
return;
|
|
322
323
|
}
|
|
323
324
|
|
|
324
325
|
// Skip CSRF check for safe methods
|
|
@@ -326,7 +327,8 @@ class RedisCSRFStore {
|
|
|
326
327
|
if (ignoreMethods.includes(method)) {
|
|
327
328
|
// Ensure token exists for this session
|
|
328
329
|
await self.get(sessionId);
|
|
329
|
-
return await next();
|
|
330
|
+
if (typeof next === 'function') return await next();
|
|
331
|
+
return;
|
|
330
332
|
}
|
|
331
333
|
|
|
332
334
|
// Get token from request (header or body)
|
|
@@ -373,7 +375,7 @@ class RedisCSRFStore {
|
|
|
373
375
|
}
|
|
374
376
|
|
|
375
377
|
// Token valid, continue pipeline
|
|
376
|
-
await next();
|
|
378
|
+
if (typeof next === 'function') await next();
|
|
377
379
|
|
|
378
380
|
} catch (error) {
|
|
379
381
|
logger.error({
|
|
@@ -441,7 +441,7 @@ class RedisRateLimiter {
|
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
// Request allowed, continue pipeline
|
|
444
|
-
await next();
|
|
444
|
+
if (typeof next === 'function') await next();
|
|
445
445
|
|
|
446
446
|
} catch (error) {
|
|
447
447
|
logger.error({
|
|
@@ -451,7 +451,7 @@ class RedisRateLimiter {
|
|
|
451
451
|
});
|
|
452
452
|
|
|
453
453
|
// On error, allow request (fail open)
|
|
454
|
-
await next();
|
|
454
|
+
if (typeof next === 'function') await next();
|
|
455
455
|
}
|
|
456
456
|
};
|
|
457
457
|
}
|