xypriss 2.3.7 → 3.1.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.
Files changed (36) hide show
  1. package/README.md +21 -19
  2. package/dist/cjs/mods/security/src/index.js +1 -1
  3. package/dist/cjs/src/cluster/modules/CrossPlatformMemory.js +2 -2
  4. package/dist/cjs/src/cluster/modules/CrossPlatformMemory.js.map +1 -1
  5. package/dist/cjs/src/middleware/built-in/BuiltInMiddleware.js +123 -14
  6. package/dist/cjs/src/middleware/built-in/BuiltInMiddleware.js.map +1 -1
  7. package/dist/cjs/src/middleware/built-in/security/BrowserOnlyProtector.js +552 -0
  8. package/dist/cjs/src/middleware/built-in/security/BrowserOnlyProtector.js.map +1 -0
  9. package/dist/cjs/src/middleware/built-in/security/RequestSignatureProtector.js +465 -0
  10. package/dist/cjs/src/middleware/built-in/security/RequestSignatureProtector.js.map +1 -0
  11. package/dist/cjs/src/middleware/built-in/security/TerminalOnlyProtector.js +477 -0
  12. package/dist/cjs/src/middleware/built-in/security/TerminalOnlyProtector.js.map +1 -0
  13. package/dist/cjs/src/middleware/security-middleware.js +257 -91
  14. package/dist/cjs/src/middleware/security-middleware.js.map +1 -1
  15. package/dist/cjs/src/server/components/fastapi/templates/redirectTemp.js +1 -1
  16. package/dist/cjs/src/server/const/default.js +1 -1
  17. package/dist/cjs/src/server/const/default.js.map +1 -1
  18. package/dist/esm/mods/security/src/index.js +1 -1
  19. package/dist/esm/src/cluster/modules/CrossPlatformMemory.js +2 -2
  20. package/dist/esm/src/cluster/modules/CrossPlatformMemory.js.map +1 -1
  21. package/dist/esm/src/middleware/built-in/BuiltInMiddleware.js +123 -14
  22. package/dist/esm/src/middleware/built-in/BuiltInMiddleware.js.map +1 -1
  23. package/dist/esm/src/middleware/built-in/security/BrowserOnlyProtector.js +550 -0
  24. package/dist/esm/src/middleware/built-in/security/BrowserOnlyProtector.js.map +1 -0
  25. package/dist/esm/src/middleware/built-in/security/RequestSignatureProtector.js +444 -0
  26. package/dist/esm/src/middleware/built-in/security/RequestSignatureProtector.js.map +1 -0
  27. package/dist/esm/src/middleware/built-in/security/TerminalOnlyProtector.js +475 -0
  28. package/dist/esm/src/middleware/built-in/security/TerminalOnlyProtector.js.map +1 -0
  29. package/dist/esm/src/middleware/security-middleware.js +257 -91
  30. package/dist/esm/src/middleware/security-middleware.js.map +1 -1
  31. package/dist/esm/src/server/components/fastapi/templates/redirectTemp.js +1 -1
  32. package/dist/esm/src/server/const/default.js +1 -1
  33. package/dist/esm/src/server/const/default.js.map +1 -1
  34. package/dist/index.d.ts +268 -10
  35. package/package.json +6 -5
  36. package/scripts/install-memory-cli.js +1 -1
@@ -34,19 +34,53 @@ class SecurityMiddleware {
34
34
  this.csrf = config.csrf !== false ? config.csrf || true : false;
35
35
  this.helmet = config.helmet !== false ? config.helmet || true : false;
36
36
  this.xss = config.xss !== false ? config.xss || true : false;
37
- this.sqlInjection = config.sqlInjection !== false ? config.sqlInjection || true : false;
38
- this.pathTraversal = config.pathTraversal !== false ? config.pathTraversal || false : false;
39
- this.commandInjection = config.commandInjection !== false ? config.commandInjection || false : false;
37
+ this.sqlInjection =
38
+ config.sqlInjection !== false ? config.sqlInjection || true : false;
39
+ this.pathTraversal =
40
+ config.pathTraversal !== false
41
+ ? config.pathTraversal || false
42
+ : false;
43
+ this.commandInjection =
44
+ config.commandInjection !== false
45
+ ? config.commandInjection || false
46
+ : false;
40
47
  this.xxe = config.xxe !== false ? config.xxe || false : false;
41
- this.ldapInjection = config.ldapInjection !== false ? config.ldapInjection || false : false;
42
- this.bruteForce = config.bruteForce !== false ? config.bruteForce || true : false;
43
- this.rateLimit = config.rateLimit !== false ? config.rateLimit || true : false;
48
+ this.ldapInjection =
49
+ config.ldapInjection !== false
50
+ ? config.ldapInjection || false
51
+ : false;
52
+ this.bruteForce =
53
+ config.bruteForce !== false ? config.bruteForce || true : false;
54
+ this.rateLimit =
55
+ config.rateLimit !== false ? config.rateLimit || true : false;
44
56
  this.cors = config.cors !== false ? config.cors || true : false;
45
- this.compression = config.compression !== false ? config.compression || true : false;
57
+ this.compression =
58
+ config.compression !== false ? config.compression || true : false;
46
59
  this.hpp = config.hpp !== false ? config.hpp || true : false;
47
- this.mongoSanitize = config.mongoSanitize !== false ? config.mongoSanitize || true : false;
60
+ this.mongoSanitize =
61
+ config.mongoSanitize !== false
62
+ ? config.mongoSanitize || true
63
+ : false;
48
64
  this.morgan = config.morgan !== false ? config.morgan || true : false;
49
- this.slowDown = config.slowDown !== false ? config.slowDown || true : false;
65
+ this.slowDown =
66
+ config.slowDown !== false ? config.slowDown || true : false;
67
+ this.browserOnly =
68
+ config.browserOnly !== false ? config.browserOnly || false : false;
69
+ this.terminalOnly =
70
+ config.terminalOnly !== false
71
+ ? config.terminalOnly || false
72
+ : false;
73
+ this.requestSignature =
74
+ config.requestSignature !== false
75
+ ? config.requestSignature || false
76
+ : false;
77
+ // Validate that both browserOnly and terminalOnly are not enabled simultaneously
78
+ const browserOnlyEnabled = this.isBrowserOnlyEnabled();
79
+ const terminalOnlyEnabled = this.isTerminalOnlyEnabled();
80
+ if (browserOnlyEnabled && terminalOnlyEnabled) {
81
+ throw new Error("Security configuration error: browserOnly and terminalOnly cannot be enabled simultaneously. " +
82
+ "Choose one access control method or disable both.");
83
+ }
50
84
  this.encryption = {
51
85
  algorithm: "AES-256-GCM",
52
86
  keySize: 32,
@@ -69,7 +103,7 @@ class SecurityMiddleware {
69
103
  entropy: "high",
70
104
  }),
71
105
  name: config.authentication?.session?.name ||
72
- "nehonix.XyPriss.sid",
106
+ "xypriss.nehonix.sid",
73
107
  cookie: {
74
108
  maxAge: 24 * 60 * 60 * 1000, // 24 hours
75
109
  secure: true,
@@ -84,45 +118,89 @@ class SecurityMiddleware {
84
118
  this.routeConfig = config.routeConfig;
85
119
  // Initialize security detectors
86
120
  this.sqlInjectionDetector = new SQLInjectionDetector({
87
- strictMode: typeof this.sqlInjection === 'object' ? this.sqlInjection.strictMode : false,
88
- contextualAnalysis: typeof this.sqlInjection === 'object' ? this.sqlInjection.contextualAnalysis : true,
89
- logAttempts: typeof this.sqlInjection === 'object' ? this.sqlInjection.logAttempts : true,
90
- falsePositiveThreshold: typeof this.sqlInjection === 'object' ? this.sqlInjection.falsePositiveThreshold : 0.6,
121
+ strictMode: typeof this.sqlInjection === "object"
122
+ ? this.sqlInjection.strictMode
123
+ : false,
124
+ contextualAnalysis: typeof this.sqlInjection === "object"
125
+ ? this.sqlInjection.contextualAnalysis
126
+ : true,
127
+ logAttempts: typeof this.sqlInjection === "object"
128
+ ? this.sqlInjection.logAttempts
129
+ : true,
130
+ falsePositiveThreshold: typeof this.sqlInjection === "object"
131
+ ? this.sqlInjection.falsePositiveThreshold
132
+ : 0.6,
91
133
  });
92
134
  this.pathTraversalDetector = new PathTraversalDetector({
93
135
  enabled: !!this.pathTraversal,
94
- strictMode: typeof this.pathTraversal === 'object' ? this.pathTraversal.strictMode : false,
95
- logAttempts: typeof this.pathTraversal === 'object' ? this.pathTraversal.logAttempts : true,
96
- blockOnDetection: typeof this.pathTraversal === 'object' ? this.pathTraversal.blockOnDetection : true,
97
- allowedPaths: typeof this.pathTraversal === 'object' ? this.pathTraversal.allowedPaths : [],
98
- allowedExtensions: typeof this.pathTraversal === 'object' ? this.pathTraversal.allowedExtensions : ['.jpg', '.png', '.pdf', '.txt'],
99
- maxDepth: typeof this.pathTraversal === 'object' ? this.pathTraversal.maxDepth : 3,
100
- falsePositiveThreshold: typeof this.pathTraversal === 'object' ? this.pathTraversal.falsePositiveThreshold : 0.6,
136
+ strictMode: typeof this.pathTraversal === "object"
137
+ ? this.pathTraversal.strictMode
138
+ : false,
139
+ logAttempts: typeof this.pathTraversal === "object"
140
+ ? this.pathTraversal.logAttempts
141
+ : true,
142
+ blockOnDetection: typeof this.pathTraversal === "object"
143
+ ? this.pathTraversal.blockOnDetection
144
+ : true,
145
+ allowedPaths: typeof this.pathTraversal === "object"
146
+ ? this.pathTraversal.allowedPaths
147
+ : [],
148
+ allowedExtensions: typeof this.pathTraversal === "object"
149
+ ? this.pathTraversal.allowedExtensions
150
+ : [".jpg", ".png", ".pdf", ".txt"],
151
+ maxDepth: typeof this.pathTraversal === "object"
152
+ ? this.pathTraversal.maxDepth
153
+ : 3,
154
+ falsePositiveThreshold: typeof this.pathTraversal === "object"
155
+ ? this.pathTraversal.falsePositiveThreshold
156
+ : 0.6,
101
157
  });
102
158
  this.commandInjectionDetector = new CommandInjectionDetector({
103
159
  enabled: !!this.commandInjection,
104
- strictMode: typeof this.commandInjection === 'object' ? this.commandInjection.strictMode : false,
105
- logAttempts: typeof this.commandInjection === 'object' ? this.commandInjection.logAttempts : true,
106
- blockOnDetection: typeof this.commandInjection === 'object' ? this.commandInjection.blockOnDetection : true,
107
- contextualAnalysis: typeof this.commandInjection === 'object' ? this.commandInjection.contextualAnalysis : true,
108
- allowedCommands: typeof this.commandInjection === 'object' ? this.commandInjection.allowedCommands : [],
109
- falsePositiveThreshold: typeof this.commandInjection === 'object' ? this.commandInjection.falsePositiveThreshold : 0.7,
160
+ strictMode: typeof this.commandInjection === "object"
161
+ ? this.commandInjection.strictMode
162
+ : false,
163
+ logAttempts: typeof this.commandInjection === "object"
164
+ ? this.commandInjection.logAttempts
165
+ : true,
166
+ blockOnDetection: typeof this.commandInjection === "object"
167
+ ? this.commandInjection.blockOnDetection
168
+ : true,
169
+ contextualAnalysis: typeof this.commandInjection === "object"
170
+ ? this.commandInjection.contextualAnalysis
171
+ : true,
172
+ allowedCommands: typeof this.commandInjection === "object"
173
+ ? this.commandInjection.allowedCommands
174
+ : [],
175
+ falsePositiveThreshold: typeof this.commandInjection === "object"
176
+ ? this.commandInjection.falsePositiveThreshold
177
+ : 0.7,
110
178
  });
111
179
  this.xxeProtector = new XXEProtector({
112
180
  enabled: !!this.xxe,
113
- strictMode: typeof this.xxe === 'object' ? this.xxe.strictMode : true,
114
- logAttempts: typeof this.xxe === 'object' ? this.xxe.logAttempts : true,
115
- blockOnDetection: typeof this.xxe === 'object' ? this.xxe.blockOnDetection : true,
116
- allowDTD: typeof this.xxe === 'object' ? this.xxe.allowDTD : false,
117
- allowExternalEntities: typeof this.xxe === 'object' ? this.xxe.allowExternalEntities : false,
118
- maxEntityExpansions: typeof this.xxe === 'object' ? this.xxe.maxEntityExpansions : 0,
181
+ strictMode: typeof this.xxe === "object" ? this.xxe.strictMode : true,
182
+ logAttempts: typeof this.xxe === "object" ? this.xxe.logAttempts : true,
183
+ blockOnDetection: typeof this.xxe === "object" ? this.xxe.blockOnDetection : true,
184
+ allowDTD: typeof this.xxe === "object" ? this.xxe.allowDTD : false,
185
+ allowExternalEntities: typeof this.xxe === "object"
186
+ ? this.xxe.allowExternalEntities
187
+ : false,
188
+ maxEntityExpansions: typeof this.xxe === "object" ? this.xxe.maxEntityExpansions : 0,
119
189
  });
120
190
  this.ldapInjectionDetector = new LDAPInjectionDetector({
121
191
  enabled: !!this.ldapInjection,
122
- strictMode: typeof this.ldapInjection === 'object' ? this.ldapInjection.strictMode : false,
123
- logAttempts: typeof this.ldapInjection === 'object' ? this.ldapInjection.logAttempts : true,
124
- blockOnDetection: typeof this.ldapInjection === 'object' ? this.ldapInjection.blockOnDetection : true,
125
- falsePositiveThreshold: typeof this.ldapInjection === 'object' ? this.ldapInjection.falsePositiveThreshold : 0.6,
192
+ strictMode: typeof this.ldapInjection === "object"
193
+ ? this.ldapInjection.strictMode
194
+ : false,
195
+ logAttempts: typeof this.ldapInjection === "object"
196
+ ? this.ldapInjection.logAttempts
197
+ : true,
198
+ blockOnDetection: typeof this.ldapInjection === "object"
199
+ ? this.ldapInjection.blockOnDetection
200
+ : true,
201
+ falsePositiveThreshold: typeof this.ldapInjection === "object"
202
+ ? this.ldapInjection.falsePositiveThreshold
203
+ : 0.6,
126
204
  });
127
205
  // Initialize all middleware instances
128
206
  this.initializeMiddleware();
@@ -135,20 +213,28 @@ class SecurityMiddleware {
135
213
  // Helmet for security headers
136
214
  if (this.helmet) {
137
215
  const helmetConfig = typeof this.helmet === "object" ? this.helmet : {};
216
+ // Prepare CSP configuration with proper merging
217
+ let cspConfig = false;
218
+ if (this.level === "maximum") {
219
+ cspConfig = {
220
+ directives: {
221
+ defaultSrc: ["'self'"],
222
+ styleSrc: ["'self'", "'unsafe-inline'"],
223
+ scriptSrc: ["'self'"],
224
+ imgSrc: ["'self'", "data:", "https:"],
225
+ },
226
+ };
227
+ }
228
+ else if (helmetConfig.contentSecurityPolicy) {
229
+ // Merge user CSP config with defaults from BuiltInMiddleware
230
+ cspConfig = helmetConfig.contentSecurityPolicy; // BuiltInMiddleware will handle merging
231
+ }
232
+ this.logger.debug("security", "Final cspConfig:", cspConfig);
138
233
  this.helmetMiddleware = BuiltInMiddleware.BuiltInMiddleware.helmet({
139
- contentSecurityPolicy: this.level === "maximum"
140
- ? {
141
- directives: {
142
- defaultSrc: ["'self'"],
143
- styleSrc: ["'self'", "'unsafe-inline'"],
144
- scriptSrc: ["'self'"],
145
- imgSrc: ["'self'", "data:", "https:"],
146
- },
147
- }
148
- : helmetConfig.contentSecurityPolicy
149
- ? helmetConfig.contentSecurityPolicy
150
- : false,
151
- hsts: this.level !== "basic" || helmetConfig.hsts ? helmetConfig.hsts : undefined,
234
+ contentSecurityPolicy: cspConfig,
235
+ hsts: this.level !== "basic" || helmetConfig.hsts
236
+ ? helmetConfig.hsts
237
+ : undefined,
152
238
  crossOriginEmbedderPolicy: this.level === "maximum",
153
239
  });
154
240
  }
@@ -190,8 +276,10 @@ class SecurityMiddleware {
190
276
  legacyHeaders: false,
191
277
  skip: (req) => {
192
278
  // Skip rate limiting for health checks and static assets
193
- return req.path === "/health" || req.path === "/ping" ||
194
- req.path.startsWith("/static/") || req.path.startsWith("/assets/");
279
+ return (req.path === "/health" ||
280
+ req.path === "/ping" ||
281
+ req.path.startsWith("/static/") ||
282
+ req.path.startsWith("/assets/"));
195
283
  },
196
284
  });
197
285
  this.logger.debug("security", `General rate limiting initialized with max: ${maxRequests} requests`);
@@ -213,6 +301,27 @@ class SecurityMiddleware {
213
301
  },
214
302
  });
215
303
  }
304
+ // Browser-only protection
305
+ if (this.isBrowserOnlyEnabled()) {
306
+ const browserOnlyConfig = typeof this.browserOnly === "object" ? this.browserOnly : {};
307
+ this.browserOnlyMiddleware =
308
+ BuiltInMiddleware.BuiltInMiddleware.browserOnly(browserOnlyConfig);
309
+ }
310
+ // Terminal-only protection
311
+ if (this.isTerminalOnlyEnabled()) {
312
+ const terminalOnlyConfig = typeof this.terminalOnly === "object" ? this.terminalOnly : {};
313
+ this.terminalOnlyMiddleware =
314
+ BuiltInMiddleware.BuiltInMiddleware.terminalOnly(terminalOnlyConfig);
315
+ }
316
+ // Request signature protection (API authentication)
317
+ if (this.requestSignature) {
318
+ const requestSignatureConfig = typeof this.requestSignature === "object" &&
319
+ this.requestSignature !== null
320
+ ? this.requestSignature
321
+ : { secret: "default-secret" }; // This will be overridden by user config
322
+ this.requestSignatureMiddleware =
323
+ BuiltInMiddleware.BuiltInMiddleware.requestSignature(requestSignatureConfig);
324
+ }
216
325
  // Compression middleware
217
326
  if (this.compression) {
218
327
  const compressionConfig = typeof this.compression === "object" ? this.compression : {};
@@ -233,19 +342,23 @@ class SecurityMiddleware {
233
342
  }
234
343
  // MongoDB injection protection
235
344
  if (this.mongoSanitize) {
236
- const mongoConfig = typeof this.mongoSanitize === "object" ? this.mongoSanitize : {};
345
+ const mongoConfig = typeof this.mongoSanitize === "object"
346
+ ? this.mongoSanitize
347
+ : {};
237
348
  this.mongoSanitizeMiddleware = BuiltInMiddleware.BuiltInMiddleware.mongoSanitize({
238
349
  replaceWith: mongoConfig.replaceWith || "_",
239
- onSanitize: mongoConfig.onSanitize || (({ req, key }) => {
240
- console.warn(`Sanitized key ${key} in request from ${req.ip}`);
241
- }),
350
+ onSanitize: mongoConfig.onSanitize ||
351
+ (({ req, key }) => {
352
+ this.logger.warn("security", `Sanitized key ${key} in request from ${req.ip}`);
353
+ }),
242
354
  });
243
355
  }
244
356
  // Morgan logging middleware
245
357
  if (this.morgan) {
246
358
  const morganConfig = typeof this.morgan === "object" ? this.morgan : {};
247
359
  this.morganMiddleware = BuiltInMiddleware.BuiltInMiddleware.morgan({
248
- skip: morganConfig.skip || ((req, res) => res.statusCode < 400),
360
+ skip: morganConfig.skip ||
361
+ ((req, res) => res.statusCode < 400),
249
362
  stream: morganConfig.stream,
250
363
  });
251
364
  }
@@ -255,10 +368,11 @@ class SecurityMiddleware {
255
368
  this.slowDownMiddleware = BuiltInMiddleware.BuiltInMiddleware.slowDown({
256
369
  windowMs: slowDownConfig.windowMs || 15 * 60 * 1000, // 15 minutes
257
370
  delayAfter: slowDownConfig.delayAfter || 100,
258
- delayMs: slowDownConfig.delayMs || ((used, req) => {
259
- const delayAfter = req.slowDown?.limit || 100;
260
- return (used - delayAfter) * 500;
261
- }),
371
+ delayMs: slowDownConfig.delayMs ||
372
+ ((used, req) => {
373
+ const delayAfter = req.slowDown?.limit || 100;
374
+ return (used - delayAfter) * 500;
375
+ }),
262
376
  });
263
377
  }
264
378
  }
@@ -277,57 +391,74 @@ class SecurityMiddleware {
277
391
  applySecurityStack(req, res, next) {
278
392
  this.logger.debug("security", "Starting security middleware stack");
279
393
  const middlewareStack = [];
280
- // 1. Compression (should be first)
394
+ // 🚨 CRITICAL: Access control middlewares FIRST (before any other processing)
395
+ // These must run before route resolution to block unwanted requests
396
+ // 1. Browser-only protection (blocks cURL and automation tools)
397
+ if (this.isBrowserOnlyEnabled() && this.browserOnlyMiddleware) {
398
+ this.logger.debug("security", "Adding browser-only middleware (FIRST)");
399
+ middlewareStack.push(this.browserOnlyMiddleware);
400
+ }
401
+ // 2. Terminal-only protection (blocks browser requests)
402
+ if (this.isTerminalOnlyEnabled() && this.terminalOnlyMiddleware) {
403
+ this.logger.debug("security", "Adding terminal-only middleware (FIRST)");
404
+ middlewareStack.push(this.terminalOnlyMiddleware);
405
+ }
406
+ // 3. Request signature protection (API authentication)
407
+ if (this.requestSignature && this.requestSignatureMiddleware) {
408
+ this.logger.debug("security", "Adding request signature middleware (FIRST)");
409
+ middlewareStack.push(this.requestSignatureMiddleware);
410
+ }
411
+ // 4. Compression (should be early but after access control)
281
412
  if (this.compression && this.compressionMiddleware) {
282
413
  this.logger.debug("security", "Adding compression middleware");
283
414
  middlewareStack.push(this.compressionMiddleware);
284
415
  }
285
- // 2. Security headers (Helmet)
416
+ // 5. Security headers (Helmet)
286
417
  if (this.helmet && this.helmetMiddleware) {
287
418
  this.logger.debug("security", "Adding helmet middleware");
288
419
  middlewareStack.push(this.helmetMiddleware);
289
420
  }
290
- // 3. CORS
421
+ // 6. CORS
291
422
  if (this.cors !== false && this.corsMiddleware) {
292
423
  this.logger.debug("security", "Adding CORS middleware");
293
424
  middlewareStack.push(this.corsMiddleware);
294
425
  }
295
- // 4. Rate limiting (brute force protection - stricter)
426
+ // 7. Rate limiting (brute force protection - stricter)
296
427
  if (this.bruteForce && this.bruteForceMiddleware) {
297
428
  this.logger.debug("security", "Adding brute force protection middleware");
298
429
  middlewareStack.push(this.bruteForceMiddleware);
299
430
  }
300
- // 5. General rate limiting (less strict)
431
+ // 8. General rate limiting (less strict)
301
432
  if (this.rateLimit && this.rateLimitMiddleware) {
302
433
  this.logger.debug("security", "Adding general rate limiting middleware");
303
434
  middlewareStack.push(this.rateLimitMiddleware);
304
435
  }
305
- // 6. HTTP Parameter Pollution protection
436
+ // 9. HTTP Parameter Pollution protection
306
437
  if (this.hpp && this.hppMiddleware) {
307
438
  this.logger.debug("security", "Adding HPP middleware");
308
439
  middlewareStack.push(this.hppMiddleware);
309
440
  }
310
- // 7. MongoDB sanitization
441
+ // 10. MongoDB sanitization
311
442
  if (this.mongoSanitize && this.mongoSanitizeMiddleware) {
312
443
  this.logger.debug("security", "Adding mongo sanitize middleware");
313
444
  middlewareStack.push(this.mongoSanitizeMiddleware);
314
445
  }
315
- // 8. Morgan logging
446
+ // 11. Morgan logging
316
447
  if (this.morgan && this.morganMiddleware) {
317
448
  this.logger.debug("security", "Adding morgan middleware");
318
449
  middlewareStack.push(this.morganMiddleware);
319
450
  }
320
- // 9. Slow down middleware
451
+ // 12. Slow down middleware
321
452
  if (this.slowDown && this.slowDownMiddleware) {
322
453
  this.logger.debug("security", "Adding slow down middleware");
323
454
  middlewareStack.push(this.slowDownMiddleware);
324
455
  }
325
- // 10. XSS protection (custom implementation)
456
+ // 13. XSS protection (custom implementation)
326
457
  if (this.xss) {
327
458
  this.logger.debug("security", "Adding XSS protection middleware");
328
459
  middlewareStack.push(this.xssProtection.bind(this));
329
460
  }
330
- // 11. CSRF protection (should be after body parsing)
461
+ // 14. CSRF protection (should be after body parsing)
331
462
  if (this.csrf && this.csrfMiddleware) {
332
463
  this.logger.debug("security", "Adding CSRF middleware");
333
464
  middlewareStack.push(this.csrfMiddleware);
@@ -507,7 +638,8 @@ class SecurityMiddleware {
507
638
  detectedPatterns.push("XSS");
508
639
  }
509
640
  // SQL Injection Detection
510
- if (this.sqlInjection && this.shouldApplySecurityModule(req, this.routeConfig?.sqlInjection)) {
641
+ if (this.sqlInjection &&
642
+ this.shouldApplySecurityModule(req, this.routeConfig?.sqlInjection)) {
511
643
  const sqlResult = this.sqlInjectionDetector.detect(original, currentPath);
512
644
  if (sqlResult.isMalicious) {
513
645
  threatDetected = true;
@@ -519,7 +651,8 @@ class SecurityMiddleware {
519
651
  }
520
652
  }
521
653
  // Path Traversal Detection
522
- if (this.pathTraversal && this.shouldApplySecurityModule(req, this.routeConfig?.pathTraversal)) {
654
+ if (this.pathTraversal &&
655
+ this.shouldApplySecurityModule(req, this.routeConfig?.pathTraversal)) {
523
656
  const pathResult = this.pathTraversalDetector.detect(original);
524
657
  if (pathResult.isMalicious) {
525
658
  threatDetected = true;
@@ -530,7 +663,8 @@ class SecurityMiddleware {
530
663
  }
531
664
  }
532
665
  // Command Injection Detection
533
- if (this.commandInjection && this.shouldApplySecurityModule(req, this.routeConfig?.commandInjection)) {
666
+ if (this.commandInjection &&
667
+ this.shouldApplySecurityModule(req, this.routeConfig?.commandInjection)) {
534
668
  const cmdResult = this.commandInjectionDetector.detect(original);
535
669
  if (cmdResult.isMalicious) {
536
670
  threatDetected = true;
@@ -541,7 +675,10 @@ class SecurityMiddleware {
541
675
  }
542
676
  }
543
677
  // XXE Detection (for XML content)
544
- if (this.xxe && this.shouldApplySecurityModule(req, this.routeConfig?.xxe) && (original.includes('<?xml') || original.includes('<!DOCTYPE'))) {
678
+ if (this.xxe &&
679
+ this.shouldApplySecurityModule(req, this.routeConfig?.xxe) &&
680
+ (original.includes("<?xml") ||
681
+ original.includes("<!DOCTYPE"))) {
545
682
  const xxeResult = this.xxeProtector.detect(original);
546
683
  if (xxeResult.isMalicious) {
547
684
  threatDetected = true;
@@ -552,7 +689,8 @@ class SecurityMiddleware {
552
689
  }
553
690
  }
554
691
  // LDAP Injection Detection
555
- if (this.ldapInjection && this.shouldApplySecurityModule(req, this.routeConfig?.ldapInjection)) {
692
+ if (this.ldapInjection &&
693
+ this.shouldApplySecurityModule(req, this.routeConfig?.ldapInjection)) {
556
694
  const ldapResult = this.ldapInjectionDetector.detect(original);
557
695
  if (ldapResult.isMalicious) {
558
696
  threatDetected = true;
@@ -617,6 +755,29 @@ class SecurityMiddleware {
617
755
  }
618
756
  return null;
619
757
  }
758
+ /**
759
+ * Check if browser-only protection is enabled
760
+ */
761
+ isBrowserOnlyEnabled() {
762
+ if (this.browserOnly === true)
763
+ return true;
764
+ if (typeof this.browserOnly === "object" && this.browserOnly !== null) {
765
+ return this.browserOnly.enable !== false; // Default to true when config provided
766
+ }
767
+ return false;
768
+ }
769
+ /**
770
+ * Check if terminal-only protection is enabled
771
+ */
772
+ isTerminalOnlyEnabled() {
773
+ if (this.terminalOnly === true)
774
+ return true;
775
+ if (typeof this.terminalOnly === "object" &&
776
+ this.terminalOnly !== null) {
777
+ return this.terminalOnly.enable !== false; // Default to true when config provided
778
+ }
779
+ return false;
780
+ }
620
781
  /**
621
782
  * Get security configuration
622
783
  */
@@ -625,6 +786,9 @@ class SecurityMiddleware {
625
786
  level: this.level,
626
787
  csrf: this.csrf,
627
788
  helmet: this.helmet,
789
+ browserOnly: this.browserOnly,
790
+ terminalOnly: this.terminalOnly,
791
+ requestSignature: this.requestSignature,
628
792
  xss: this.xss,
629
793
  sqlInjection: this.sqlInjection,
630
794
  pathTraversal: this.pathTraversal,
@@ -649,7 +813,7 @@ class SecurityMiddleware {
649
813
  */
650
814
  matchesRoute(requestPath, requestMethod, pattern) {
651
815
  // Handle RoutePattern object
652
- if (typeof pattern === 'object' && 'path' in pattern) {
816
+ if (typeof pattern === "object" && "path" in pattern) {
653
817
  const routePattern = pattern;
654
818
  // Check method if specified
655
819
  if (routePattern.methods && routePattern.methods.length > 0) {
@@ -666,25 +830,25 @@ class SecurityMiddleware {
666
830
  // Handle string patterns with wildcards
667
831
  const patternStr = pattern;
668
832
  // Normalize paths by removing trailing slashes for comparison
669
- const normalizedRequestPath = requestPath.replace(/\/$/, '');
670
- const normalizedPattern = patternStr.replace(/\/$/, '');
833
+ const normalizedRequestPath = requestPath.replace(/\/$/, "");
834
+ const normalizedPattern = patternStr.replace(/\/$/, "");
671
835
  // Exact match (after normalization)
672
836
  if (normalizedPattern === normalizedRequestPath) {
673
837
  return true;
674
838
  }
675
839
  // Wildcard matching (e.g., /api/* matches /api/anything)
676
- if (patternStr.includes('*')) {
840
+ if (patternStr.includes("*")) {
677
841
  // Handle trailing /* specially to match with or without trailing slash
678
- if (patternStr.endsWith('/*')) {
842
+ if (patternStr.endsWith("/*")) {
679
843
  const prefix = patternStr.slice(0, -2); // Remove /*
680
844
  // Match if requestPath starts with prefix, optionally followed by /
681
- const regex = new RegExp(`^${prefix.replace(/[.+?^${}()|[\]\\]/g, '\\$&')}(?:/.*)?$`);
845
+ const regex = new RegExp(`^${prefix.replace(/[.+?^${}()|[\]\\]/g, "\\$&")}(?:/.*)?$`);
682
846
  return regex.test(requestPath);
683
847
  }
684
848
  else {
685
849
  const regexPattern = patternStr
686
- .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *
687
- .replace(/\*/g, '.*'); // Convert * to .*
850
+ .replace(/[.+?^${}()|[\]\\]/g, "\\$&") // Escape special regex chars except *
851
+ .replace(/\*/g, ".*"); // Convert * to .*
688
852
  const regex = new RegExp(`^${regexPattern}$`);
689
853
  return regex.test(requestPath);
690
854
  }
@@ -702,17 +866,19 @@ class SecurityMiddleware {
702
866
  if (!moduleConfig) {
703
867
  return true; // Apply by default if no route config
704
868
  }
705
- const requestPath = req.path || req.url || '';
706
- const requestMethod = req.method || 'GET';
869
+ const requestPath = req.path || req.url || "";
870
+ const requestMethod = req.method || "GET";
707
871
  // Check includeRoutes first (whitelist approach)
708
- if (moduleConfig.includeRoutes && moduleConfig.includeRoutes.length > 0) {
872
+ if (moduleConfig.includeRoutes &&
873
+ moduleConfig.includeRoutes.length > 0) {
709
874
  // Only apply if route is in the include list
710
- return moduleConfig.includeRoutes.some(pattern => this.matchesRoute(requestPath, requestMethod, pattern));
875
+ return moduleConfig.includeRoutes.some((pattern) => this.matchesRoute(requestPath, requestMethod, pattern));
711
876
  }
712
877
  // Check excludeRoutes (blacklist approach)
713
- if (moduleConfig.excludeRoutes && moduleConfig.excludeRoutes.length > 0) {
878
+ if (moduleConfig.excludeRoutes &&
879
+ moduleConfig.excludeRoutes.length > 0) {
714
880
  // Don't apply if route is in the exclude list
715
- const isExcluded = moduleConfig.excludeRoutes.some(pattern => this.matchesRoute(requestPath, requestMethod, pattern));
881
+ const isExcluded = moduleConfig.excludeRoutes.some((pattern) => this.matchesRoute(requestPath, requestMethod, pattern));
716
882
  return !isExcluded;
717
883
  }
718
884
  return true; // Apply by default