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