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 +21 -22
- package/package.json +6 -2
- package/src/patterns/index.js +64 -62
- package/src/patterns/prototype-pollution.js +1 -0
- package/src/patterns/sql-injection.js +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A comprehensive security sanitization library for Model Context Protocol (MCP) s
|
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/mcp-sanitizer)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[](./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**:
|
|
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
|
-
|
|
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 {
|
|
94
|
+
const { createExpressMiddleware } = require('mcp-sanitizer/middleware/express');
|
|
95
95
|
|
|
96
96
|
const app = express();
|
|
97
97
|
app.use(express.json());
|
|
98
98
|
|
|
99
|
-
//
|
|
100
|
-
app.use(
|
|
99
|
+
// Apply middleware with defaults
|
|
100
|
+
app.use(createExpressMiddleware());
|
|
101
101
|
|
|
102
102
|
// Or specify configuration
|
|
103
|
-
app.use(
|
|
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
|
|
119
|
+
const mcpSanitizerPlugin = require('mcp-sanitizer/middleware/fastify');
|
|
120
120
|
|
|
121
121
|
// Register as plugin
|
|
122
|
-
fastify.register(
|
|
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 |
|
|
409
|
-
|
|
410
|
-
| **XSS Vectors** |
|
|
411
|
-
| **SQL Injection** |
|
|
412
|
-
| **Command Injection** |
|
|
413
|
-
| **Path Traversal** |
|
|
414
|
-
| **ReDoS Protection** |
|
|
415
|
-
| **Prototype Pollution** |
|
|
416
|
-
| **Memory Safety** |
|
|
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. ✅ **
|
|
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.
|
|
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
|
}
|
package/src/patterns/index.js
CHANGED
|
@@ -227,81 +227,83 @@ function createPatternDetector (config = {}) {
|
|
|
227
227
|
customPatterns = []
|
|
228
228
|
} = config;
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
301
|
-
|
|
299
|
+
return results;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
detect,
|
|
302
304
|
|
|
303
305
|
isSecure: (input, options = {}) => {
|
|
304
|
-
return !
|
|
306
|
+
return !detect(input, options).detected;
|
|
305
307
|
}
|
|
306
308
|
};
|
|
307
309
|
}
|
|
@@ -153,7 +153,7 @@ const ENCODED_PATTERNS = [
|
|
|
153
153
|
* Bypass techniques
|
|
154
154
|
*/
|
|
155
155
|
const BYPASS_PATTERNS = [
|
|
156
|
-
/\s
|
|
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
|