mcp-sanitizer 1.3.0 → 1.3.2

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/README.md CHANGED
@@ -4,7 +4,7 @@ A comprehensive security sanitization library for Model Context Protocol (MCP) s
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/mcp-sanitizer.svg)](https://badge.fury.io/js/mcp-sanitizer)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![Security Tests](https://img.shields.io/badge/Security%20Tests-600%2B-brightgreen)](./test)
7
+ [![Security Tests](https://img.shields.io/badge/Security%20Tests-1100%2B-brightgreen)](./test)
8
8
 
9
9
  ## 🔒 Security Features
10
10
 
@@ -17,7 +17,7 @@ MCP Sanitizer provides comprehensive, defense-in-depth protection:
17
17
  - ✅ **Framework Integration**: Express, Fastify, and Koa middleware with `skipPaths` support
18
18
  - ✅ **Security Policies**: Pre-configured policies (STRICT, MODERATE, PERMISSIVE, DEVELOPMENT, PRODUCTION)
19
19
  - ✅ **Comprehensive Validation**: Checking 42+ attack vectors across 12 validation layers in <1ms
20
- - ✅ **Comprehensive Testing**: 670 tests with 78% coverage, zero false negatives, sub-millisecond performance
20
+ - ✅ **Comprehensive Testing**: 1114 tests with 93% code coverage, sub-millisecond performance
21
21
 
22
22
  ### Security Philosophy
23
23
  While we maintain rigorous security standards and comprehensive test coverage, we acknowledge that:
@@ -82,7 +82,7 @@ const customSanitizer = new MCPSanitizer({
82
82
  policy: 'MODERATE',
83
83
  maxStringLength: 15000,
84
84
  allowedProtocols: ['https', 'mcp'],
85
- blockSeverity: 'MEDIUM' // Block medium severity and above
85
+ blockOnSeverity: 'medium' // Block medium severity and above
86
86
  });
87
87
  ```
88
88
  ## Framework Middleware
@@ -91,16 +91,16 @@ const customSanitizer = new MCPSanitizer({
91
91
 
92
92
  ```javascript
93
93
  const express = require('express');
94
- const { createMCPMiddleware } = require('mcp-sanitizer');
94
+ const { createExpressMiddleware } = require('mcp-sanitizer/middleware/express');
95
95
 
96
96
  const app = express();
97
97
  app.use(express.json());
98
98
 
99
- // Auto-detect framework and apply middleware
100
- app.use(createMCPMiddleware());
99
+ // Apply middleware with defaults
100
+ app.use(createExpressMiddleware());
101
101
 
102
102
  // Or specify configuration
103
- app.use(createMCPMiddleware({
103
+ app.use(createExpressMiddleware({
104
104
  policy: 'PRODUCTION',
105
105
  mode: 'sanitize', // or 'block'
106
106
  skipPaths: ['/health', '/metrics'] // Skip sanitization for these paths
@@ -116,19 +116,19 @@ app.post('/tools/:toolName/execute', (req, res) => {
116
116
 
117
117
  ```javascript
118
118
  const fastify = require('fastify')();
119
- const { createFastifyPlugin } = require('mcp-sanitizer');
119
+ const mcpSanitizerPlugin = require('mcp-sanitizer/middleware/fastify');
120
120
 
121
121
  // Register as plugin
122
- fastify.register(createFastifyPlugin({
122
+ fastify.register(mcpSanitizerPlugin, {
123
123
  policy: 'MODERATE'
124
- }));
124
+ });
125
125
  ```
126
126
 
127
127
  ### Koa
128
128
 
129
129
  ```javascript
130
130
  const Koa = require('koa');
131
- const { createKoaMiddleware } = require('mcp-sanitizer');
131
+ const { createKoaMiddleware } = require('mcp-sanitizer/middleware/koa');
132
132
 
133
133
  const app = new Koa();
134
134
  app.use(createKoaMiddleware({
@@ -405,15 +405,15 @@ node benchmark/skip-paths-performance.js
405
405
 
406
406
  ### 🔒 Security Testing Coverage
407
407
 
408
- | Category | Test Cases | Coverage |
409
- |----------|------------|----------|
410
- | **XSS Vectors** | 13 | DOM-based, attribute injection, polyglots |
411
- | **SQL Injection** | 10 | All major databases, blind/time-based |
412
- | **Command Injection** | 10 | Shell commands, environment vars, process substitution |
413
- | **Path Traversal** | 9 | Directory traversal, absolute paths, UNC |
414
- | **ReDoS Protection** | 22 | Polynomial backtracking, timeout guards |
415
- | **Prototype Pollution** | 3 | `__proto__`, constructor, prototype |
416
- | **Memory Safety** | 3 | Bounded memory usage under attack |
408
+ | Category | Coverage |
409
+ |----------|----------|
410
+ | **XSS Vectors** | DOM-based, attribute injection, polyglots |
411
+ | **SQL Injection** | All major databases, blind/time-based, bypass techniques |
412
+ | **Command Injection** | Shell metacharacters, environment vars, process substitution |
413
+ | **Path Traversal** | Directory traversal, absolute paths, UNC |
414
+ | **ReDoS Protection** | Polynomial backtracking, timeout guards |
415
+ | **Prototype Pollution** | `__proto__`, constructor, prototype, encoding bypass |
416
+ | **Memory Safety** | Bounded memory usage under attack |
417
417
 
418
418
  ### 🎯 Security Best Practices
419
419
 
@@ -422,8 +422,7 @@ node benchmark/skip-paths-performance.js
422
422
  3. ✅ **Monitor blocked attempts** in production for security insights
423
423
  4. ✅ **Implement defense-in-depth** - multiple security layers
424
424
  5. ✅ **Test with your attack vectors** using provided benchmarks
425
- 6. ✅ **Use crypto.timingSafeEqual()** for secret comparison
426
- 7. ✅ **Enable rate limiting** at infrastructure layer
425
+ 6. ✅ **Enable rate limiting** at infrastructure layer
427
426
 
428
427
  ### 📝 Security Resources
429
428
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-sanitizer",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Comprehensive security sanitization library for Model Context Protocol (MCP) servers with trusted security libraries",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -62,5 +62,9 @@
62
62
  "bugs": {
63
63
  "url": "https://github.com/starman69/mcp-sanitizer/issues"
64
64
  },
65
- "homepage": "https://github.com/starman69/mcp-sanitizer#readme"
65
+ "homepage": "https://github.com/starman69/mcp-sanitizer#readme",
66
+ "overrides": {
67
+ "qs": ">=6.14.2",
68
+ "flatted": ">=3.4.2"
69
+ }
66
70
  }
@@ -227,81 +227,83 @@ function createPatternDetector (config = {}) {
227
227
  customPatterns = []
228
228
  } = config;
229
229
 
230
- return {
231
- detect: (input, options = {}) => {
232
- const mergedOptions = { ...options, strictMode, customPatterns };
233
-
234
- if (!enableCommandInjection &&
235
- !enableSQLInjection &&
236
- !enablePrototypePollution &&
237
- !enableTemplateInjection &&
238
- !enableNoSQLInjection) {
239
- return { detected: false, severity: null, patterns: [] };
240
- }
230
+ const detect = (input, options = {}) => {
231
+ const mergedOptions = { ...options, strictMode, customPatterns };
232
+
233
+ if (!enableCommandInjection &&
234
+ !enableSQLInjection &&
235
+ !enablePrototypePollution &&
236
+ !enableTemplateInjection &&
237
+ !enableNoSQLInjection) {
238
+ return { detected: false, severity: null, patterns: [] };
239
+ }
241
240
 
242
- // Run only enabled detectors
243
- const results = {
244
- detected: false,
245
- severity: null,
246
- patterns: [],
247
- detectionResults: {}
248
- };
249
-
250
- if (enableCommandInjection) {
251
- const result = commandInjection.detectCommandInjection(input, mergedOptions);
252
- if (result.detected) {
253
- results.detected = true;
254
- results.patterns.push(...result.patterns);
255
- results.severity = getHigherSeverity(results.severity, result.severity);
256
- }
257
- results.detectionResults.commandInjection = result;
241
+ // Run only enabled detectors
242
+ const results = {
243
+ detected: false,
244
+ severity: null,
245
+ patterns: [],
246
+ detectionResults: {}
247
+ };
248
+
249
+ if (enableCommandInjection) {
250
+ const result = commandInjection.detectCommandInjection(input, mergedOptions);
251
+ if (result.detected) {
252
+ results.detected = true;
253
+ results.patterns.push(...result.patterns);
254
+ results.severity = getHigherSeverity(results.severity, result.severity);
258
255
  }
256
+ results.detectionResults.commandInjection = result;
257
+ }
259
258
 
260
- if (enableSQLInjection) {
261
- const result = sqlInjection.detectSQLInjection(input, mergedOptions);
262
- if (result.detected) {
263
- results.detected = true;
264
- results.patterns.push(...result.patterns);
265
- results.severity = getHigherSeverity(results.severity, result.severity);
266
- }
267
- results.detectionResults.sqlInjection = result;
259
+ if (enableSQLInjection) {
260
+ const result = sqlInjection.detectSQLInjection(input, mergedOptions);
261
+ if (result.detected) {
262
+ results.detected = true;
263
+ results.patterns.push(...result.patterns);
264
+ results.severity = getHigherSeverity(results.severity, result.severity);
268
265
  }
266
+ results.detectionResults.sqlInjection = result;
267
+ }
269
268
 
270
- if (enablePrototypePollution) {
271
- const result = prototypePollution.detectPrototypePollution(input, mergedOptions);
272
- if (result.detected) {
273
- results.detected = true;
274
- results.patterns.push(...result.patterns);
275
- results.severity = getHigherSeverity(results.severity, result.severity);
276
- }
277
- results.detectionResults.prototypePollution = result;
269
+ if (enablePrototypePollution) {
270
+ const result = prototypePollution.detectPrototypePollution(input, mergedOptions);
271
+ if (result.detected) {
272
+ results.detected = true;
273
+ results.patterns.push(...result.patterns);
274
+ results.severity = getHigherSeverity(results.severity, result.severity);
278
275
  }
276
+ results.detectionResults.prototypePollution = result;
277
+ }
279
278
 
280
- if (enableTemplateInjection) {
281
- const result = templateInjection.detectTemplateInjection(input, mergedOptions);
282
- if (result.detected) {
283
- results.detected = true;
284
- results.patterns.push(...result.patterns);
285
- results.severity = getHigherSeverity(results.severity, result.severity);
286
- }
287
- results.detectionResults.templateInjection = result;
279
+ if (enableTemplateInjection) {
280
+ const result = templateInjection.detectTemplateInjection(input, mergedOptions);
281
+ if (result.detected) {
282
+ results.detected = true;
283
+ results.patterns.push(...result.patterns);
284
+ results.severity = getHigherSeverity(results.severity, result.severity);
288
285
  }
286
+ results.detectionResults.templateInjection = result;
287
+ }
289
288
 
290
- if (enableNoSQLInjection) {
291
- const result = nosqlInjection.detectNoSQLInjection(input, mergedOptions);
292
- if (result.detected) {
293
- results.detected = true;
294
- results.patterns.push(...result.patterns);
295
- results.severity = getHigherSeverity(results.severity, result.severity);
296
- }
297
- results.detectionResults.nosqlInjection = result;
289
+ if (enableNoSQLInjection) {
290
+ const result = nosqlInjection.detectNoSQLInjection(input, mergedOptions);
291
+ if (result.detected) {
292
+ results.detected = true;
293
+ results.patterns.push(...result.patterns);
294
+ results.severity = getHigherSeverity(results.severity, result.severity);
298
295
  }
296
+ results.detectionResults.nosqlInjection = result;
297
+ }
299
298
 
300
- return results;
301
- },
299
+ return results;
300
+ };
301
+
302
+ return {
303
+ detect,
302
304
 
303
305
  isSecure: (input, options = {}) => {
304
- return !this.detect(input, options).detected;
306
+ return !detect(input, options).detected;
305
307
  }
306
308
  };
307
309
  }
@@ -364,6 +364,7 @@ function checkEncodingBypassPatterns (input) {
364
364
  const detected = [];
365
365
 
366
366
  for (const pattern of ENCODING_BYPASS_PATTERNS) {
367
+ pattern.lastIndex = 0;
367
368
  if (pattern.test(input)) {
368
369
  detected.push(`encoding_bypass:${pattern.source}`);
369
370
  }
@@ -153,7 +153,7 @@ const ENCODED_PATTERNS = [
153
153
  * Bypass techniques
154
154
  */
155
155
  const BYPASS_PATTERNS = [
156
- /\s+/g, // Multiple spaces
156
+ /\s{2,}/g, // Multiple consecutive spaces (bypass technique)
157
157
  /\/\*(?:[^*]|\*(?!\/))*\*\//g, // Inline comments (optimized)
158
158
  /\bunion\s*\/\*[^*]{0,50}\*\//gi, // Comment-separated keywords (bounded)
159
159
  /\bselect\s*\/\*[^*]{0,50}\*\//gi, // Bounded to prevent backtracking