@devilsdev/rag-pipeline-utils 2.2.0 → 2.3.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.
- package/README.md +323 -722
- package/contracts/contract-schema.json +189 -0
- package/contracts/reranker-contract.json +69 -0
- package/dist/index.cjs +19 -0
- package/dist/index.mjs +15 -1
- package/package.json +4 -2
- package/src/cli/commands/wizard.js +627 -0
- package/src/cli/input-sanitization-middleware.js +356 -0
- package/src/cli/templates/development.template.json +64 -0
- package/src/cli/templates/minimal.template.json +33 -0
- package/src/cli/templates/production.template.json +104 -0
- package/src/cli/templates/testing.template.json +45 -0
- package/src/config/connection-pool-config.json +259 -0
- package/src/config/memory-optimization.js +414 -0
- package/src/config/redaction-patterns.json +150 -0
- package/src/config/sanitization-rules.json +323 -0
- package/src/core/performance/streaming-safeguards.js +10 -1
- package/src/core/plugin-registry.js +275 -0
- package/src/dag/core/error-context.js +391 -0
- package/src/dag/core/execution-scheduler.js +350 -0
- package/src/dag/core/topology-validation.js +415 -0
- package/src/dag/dag-engine.js +26 -424
- package/src/dev/README.md +126 -0
- package/src/dev/dev-server.js +356 -0
- package/src/dev/examples/hot-reload-usage.js +355 -0
- package/src/dev/hot-reload.js +807 -0
- package/src/enterprise/sso-integration.js +184 -15
- package/src/index.js +19 -0
- package/src/observability/event-logger.js +62 -2
- package/src/plugins/optimized-openai-embedder.js +169 -0
- package/src/reranker/llm-reranker.js +30 -4
- package/src/security/jwt-config.js +431 -0
- package/src/security/jwt-validator.js +701 -0
- package/src/utils/api-client-pool-integration.js +436 -0
- package/src/utils/batch-processor.js +710 -0
- package/src/utils/connection-pool.js +605 -0
- package/src/utils/contract-validator.js +460 -0
- package/src/utils/error-formatter.js +603 -0
- package/src/utils/examples/error-formatter-usage.js +351 -0
- package/src/utils/input-sanitizer.js +967 -0
- package/src/utils/memory-monitor.js +800 -0
- package/src/utils/rate-limiter.js +244 -0
- package/src/utils/secure-logger-config.js +352 -0
- package/src/utils/secure-logger.js +576 -0
- package/src/utils/secure-logging.js +338 -0
- package/src/utils/structured-logger.js +32 -5
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Input Sanitizer
|
|
5
|
+
*
|
|
6
|
+
* Comprehensive input validation and sanitization utility to prevent:
|
|
7
|
+
* - Cross-Site Scripting (XSS) attacks
|
|
8
|
+
* - SQL Injection attacks
|
|
9
|
+
* - Command Injection attacks
|
|
10
|
+
* - Path Traversal attacks
|
|
11
|
+
* - NoSQL Injection attacks
|
|
12
|
+
* - LDAP Injection attacks
|
|
13
|
+
*
|
|
14
|
+
* Performance optimized for high-throughput scenarios with caching
|
|
15
|
+
* and efficient regex patterns.
|
|
16
|
+
*
|
|
17
|
+
* @module utils/input-sanitizer
|
|
18
|
+
* @since 2.3.0
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sanitization rules enum
|
|
23
|
+
*/
|
|
24
|
+
const SanitizationRules = {
|
|
25
|
+
NONE: 'none', // No sanitization
|
|
26
|
+
HTML: 'html', // HTML escape
|
|
27
|
+
HTML_STRICT: 'html_strict', // Remove all HTML tags
|
|
28
|
+
XSS: 'xss', // XSS prevention (aggressive)
|
|
29
|
+
SQL: 'sql', // SQL injection prevention
|
|
30
|
+
COMMAND: 'command', // Command injection prevention
|
|
31
|
+
PATH: 'path', // Path traversal prevention
|
|
32
|
+
URL: 'url', // URL validation and sanitization
|
|
33
|
+
EMAIL: 'email', // Email validation
|
|
34
|
+
ALPHANUMERIC: 'alphanumeric', // Only letters and numbers
|
|
35
|
+
UUID: 'uuid', // UUID validation
|
|
36
|
+
INTEGER: 'integer', // Integer validation
|
|
37
|
+
FLOAT: 'float', // Float validation
|
|
38
|
+
BOOLEAN: 'boolean', // Boolean validation
|
|
39
|
+
JSON: 'json', // JSON validation
|
|
40
|
+
BASE64: 'base64', // Base64 validation
|
|
41
|
+
HEX: 'hex', // Hexadecimal validation
|
|
42
|
+
SLUG: 'slug', // Slug format (URL-friendly)
|
|
43
|
+
DOMAIN: 'domain', // Domain name validation
|
|
44
|
+
IPV4: 'ipv4', // IPv4 address validation
|
|
45
|
+
IPV6: 'ipv6', // IPv6 address validation
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Common validation patterns (compiled for performance)
|
|
50
|
+
*/
|
|
51
|
+
const ValidationPatterns = {
|
|
52
|
+
email:
|
|
53
|
+
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
|
|
54
|
+
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
55
|
+
url: /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)$/,
|
|
56
|
+
slug: /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
|
|
57
|
+
domain:
|
|
58
|
+
/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/i,
|
|
59
|
+
ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
|
|
60
|
+
ipv6: /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
|
|
61
|
+
alphanumeric: /^[a-zA-Z0-9]+$/,
|
|
62
|
+
integer: /^-?\d+$/,
|
|
63
|
+
float: /^-?\d+(\.\d+)?$/,
|
|
64
|
+
base64: /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/,
|
|
65
|
+
hex: /^[0-9a-fA-F]+$/,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Dangerous patterns that indicate attacks (compiled for performance)
|
|
70
|
+
*/
|
|
71
|
+
const DangerousPatterns = {
|
|
72
|
+
// XSS patterns
|
|
73
|
+
xss: [
|
|
74
|
+
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
|
|
75
|
+
/javascript:/gi,
|
|
76
|
+
/on\w+\s*=/gi, // Event handlers: onclick=, onload=, etc.
|
|
77
|
+
/<iframe/gi,
|
|
78
|
+
/<object/gi,
|
|
79
|
+
/<embed/gi,
|
|
80
|
+
/<applet/gi,
|
|
81
|
+
/eval\s*\(/gi,
|
|
82
|
+
/expression\s*\(/gi,
|
|
83
|
+
/<meta/gi,
|
|
84
|
+
/<link/gi,
|
|
85
|
+
/alert\s*\(/gi, // Remove alert() function calls
|
|
86
|
+
/confirm\s*\(/gi, // Remove confirm() function calls
|
|
87
|
+
/prompt\s*\(/gi, // Remove prompt() function calls
|
|
88
|
+
],
|
|
89
|
+
|
|
90
|
+
// SQL injection patterns
|
|
91
|
+
sql: [
|
|
92
|
+
/(\bor\b|\band\b)\s+['"]?\d+['"]?\s*=\s*['"]?\d+/gi,
|
|
93
|
+
/union\s+select/gi,
|
|
94
|
+
/;\s*(drop|delete|insert|update|create|alter|exec|execute)\s+/gi,
|
|
95
|
+
/xp_cmdshell/gi,
|
|
96
|
+
/waitfor\s+delay/gi,
|
|
97
|
+
/benchmark\s*\(/gi,
|
|
98
|
+
],
|
|
99
|
+
|
|
100
|
+
// Command injection patterns
|
|
101
|
+
command: [
|
|
102
|
+
/[;&|`$()]/g,
|
|
103
|
+
/\$\{[^}]+\}/g, // Template literals
|
|
104
|
+
/\$\([^)]+\)/g, // Command substitution
|
|
105
|
+
/>\s*\/dev\//gi,
|
|
106
|
+
/\|\s*tee\b/gi,
|
|
107
|
+
],
|
|
108
|
+
|
|
109
|
+
// Path traversal patterns
|
|
110
|
+
pathTraversal: [
|
|
111
|
+
/\.\.[/\\]/g,
|
|
112
|
+
/\.\.[/\\]\.\./g,
|
|
113
|
+
/\/\.\.\//g,
|
|
114
|
+
/\\\.\.\\/g,
|
|
115
|
+
/%2e%2e[/\\]/gi,
|
|
116
|
+
/%252e%252e/gi,
|
|
117
|
+
],
|
|
118
|
+
|
|
119
|
+
// NoSQL injection patterns
|
|
120
|
+
nosql: [
|
|
121
|
+
/\$where/gi,
|
|
122
|
+
/\$ne\b/gi,
|
|
123
|
+
/\$gt\b/gi,
|
|
124
|
+
/\$lt\b/gi,
|
|
125
|
+
/\$or\b/gi,
|
|
126
|
+
/\$and\b/gi,
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* HTML entity map for escaping
|
|
132
|
+
*/
|
|
133
|
+
const HtmlEntityMap = {
|
|
134
|
+
'&': '&',
|
|
135
|
+
'<': '<',
|
|
136
|
+
'>': '>',
|
|
137
|
+
'"': '"',
|
|
138
|
+
'\'': ''',
|
|
139
|
+
'/': '/',
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Input Sanitizer Class
|
|
144
|
+
*/
|
|
145
|
+
class InputSanitizer {
|
|
146
|
+
constructor(options = {}) {
|
|
147
|
+
this.options = {
|
|
148
|
+
// Performance optimization
|
|
149
|
+
enableCache: options.enableCache !== false,
|
|
150
|
+
cacheSize: options.cacheSize || 1000,
|
|
151
|
+
cacheTTL: options.cacheTTL || 60000, // 1 minute
|
|
152
|
+
|
|
153
|
+
// Default rules
|
|
154
|
+
defaultStringRule: options.defaultStringRule || SanitizationRules.HTML,
|
|
155
|
+
defaultObjectDepth: options.defaultObjectDepth || 10,
|
|
156
|
+
|
|
157
|
+
// Strictness
|
|
158
|
+
throwOnInvalid: options.throwOnInvalid || false,
|
|
159
|
+
stripInvalid: options.stripInvalid !== false,
|
|
160
|
+
|
|
161
|
+
// Custom patterns
|
|
162
|
+
customPatterns: options.customPatterns || {},
|
|
163
|
+
customRules: options.customRules || {},
|
|
164
|
+
|
|
165
|
+
// Statistics
|
|
166
|
+
trackStats: options.trackStats !== false,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Initialize cache
|
|
170
|
+
this.cache = new Map();
|
|
171
|
+
this.cacheAccessTimes = new Map();
|
|
172
|
+
|
|
173
|
+
// Statistics
|
|
174
|
+
this.stats = {
|
|
175
|
+
sanitized: 0,
|
|
176
|
+
cached: 0,
|
|
177
|
+
blocked: 0,
|
|
178
|
+
validationErrors: 0,
|
|
179
|
+
averageTime: 0,
|
|
180
|
+
totalTime: 0,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Start cache cleanup if enabled
|
|
184
|
+
if (this.options.enableCache) {
|
|
185
|
+
this._startCacheCleanup();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Main sanitization function
|
|
191
|
+
*
|
|
192
|
+
* @param {*} input - Input to sanitize
|
|
193
|
+
* @param {string|Object} rules - Sanitization rules or config
|
|
194
|
+
* @returns {*} Sanitized input
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* // Simple sanitization
|
|
198
|
+
* const safe = sanitizer.sanitize('<script>alert("xss")</script>', 'html');
|
|
199
|
+
*
|
|
200
|
+
* // With rules object
|
|
201
|
+
* const safe = sanitizer.sanitize({
|
|
202
|
+
* username: '<script>hack</script>',
|
|
203
|
+
* email: 'user@example.com',
|
|
204
|
+
* age: '25'
|
|
205
|
+
* }, {
|
|
206
|
+
* username: 'xss',
|
|
207
|
+
* email: 'email',
|
|
208
|
+
* age: 'integer'
|
|
209
|
+
* });
|
|
210
|
+
*/
|
|
211
|
+
sanitize(input, rules = null) {
|
|
212
|
+
// Use high-resolution timing
|
|
213
|
+
let startTime;
|
|
214
|
+
if (typeof process !== 'undefined' && process.hrtime) {
|
|
215
|
+
startTime = process.hrtime.bigint();
|
|
216
|
+
} else if (
|
|
217
|
+
typeof performance !== 'undefined' &&
|
|
218
|
+
typeof performance.now === 'function'
|
|
219
|
+
) {
|
|
220
|
+
startTime = performance.now();
|
|
221
|
+
} else {
|
|
222
|
+
startTime = Date.now();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
// Handle null/undefined
|
|
227
|
+
if (input === null || input === undefined) {
|
|
228
|
+
return input;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Determine sanitization strategy
|
|
232
|
+
let result;
|
|
233
|
+
|
|
234
|
+
if (typeof input === 'string') {
|
|
235
|
+
result = this._sanitizeString(
|
|
236
|
+
input,
|
|
237
|
+
rules || this.options.defaultStringRule,
|
|
238
|
+
);
|
|
239
|
+
} else if (Array.isArray(input)) {
|
|
240
|
+
result = this._sanitizeArray(input, rules);
|
|
241
|
+
} else if (typeof input === 'object') {
|
|
242
|
+
result = this._sanitizeObject(input, rules, 0);
|
|
243
|
+
} else {
|
|
244
|
+
// Primitives (number, boolean, etc.)
|
|
245
|
+
result = input;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Update statistics
|
|
249
|
+
if (this.options.trackStats) {
|
|
250
|
+
let duration;
|
|
251
|
+
if (
|
|
252
|
+
typeof process !== 'undefined' &&
|
|
253
|
+
process.hrtime &&
|
|
254
|
+
process.hrtime.bigint
|
|
255
|
+
) {
|
|
256
|
+
try {
|
|
257
|
+
const endTime = process.hrtime.bigint();
|
|
258
|
+
// Convert nanoseconds to milliseconds
|
|
259
|
+
duration = Number(endTime - startTime) / 1000000;
|
|
260
|
+
} catch (e) {
|
|
261
|
+
// Fallback if hrtime.bigint() fails (e.g., in test environments with fake timers)
|
|
262
|
+
duration = 0.001; // Minimum 1 microsecond
|
|
263
|
+
}
|
|
264
|
+
} else if (
|
|
265
|
+
typeof performance !== 'undefined' &&
|
|
266
|
+
typeof performance.now === 'function'
|
|
267
|
+
) {
|
|
268
|
+
duration = performance.now() - startTime;
|
|
269
|
+
} else {
|
|
270
|
+
duration = Date.now() - startTime;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Ensure duration is at least 0.001ms (1 microsecond) to avoid zero averages
|
|
274
|
+
if (duration === 0 || !isFinite(duration)) {
|
|
275
|
+
duration = 0.001;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
this.stats.sanitized++;
|
|
279
|
+
this.stats.totalTime += duration;
|
|
280
|
+
this.stats.averageTime = this.stats.totalTime / this.stats.sanitized;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return result;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
// Always throw critical security errors regardless of throwOnInvalid setting
|
|
286
|
+
if (error.message) {
|
|
287
|
+
// Path traversal is a critical security violation - always throw
|
|
288
|
+
if (error.message.includes('Potential path traversal detected')) {
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
291
|
+
// Depth errors should also always throw
|
|
292
|
+
if (error.message.includes('Maximum object depth exceeded')) {
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (this.options.throwOnInvalid) {
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this.stats.validationErrors++;
|
|
302
|
+
return this.options.stripInvalid ? null : input;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Sanitize string input
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
_sanitizeString(str, rule) {
|
|
311
|
+
if (typeof str !== 'string') {
|
|
312
|
+
return str;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Check cache
|
|
316
|
+
if (this.options.enableCache) {
|
|
317
|
+
const cacheKey = `${rule}:${str}`;
|
|
318
|
+
if (this.cache.has(cacheKey)) {
|
|
319
|
+
this.cacheAccessTimes.set(cacheKey, Date.now());
|
|
320
|
+
this.stats.cached++;
|
|
321
|
+
return this.cache.get(cacheKey);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
let result;
|
|
326
|
+
|
|
327
|
+
switch (rule) {
|
|
328
|
+
case SanitizationRules.NONE:
|
|
329
|
+
result = str;
|
|
330
|
+
break;
|
|
331
|
+
|
|
332
|
+
case SanitizationRules.HTML:
|
|
333
|
+
result = this._escapeHtml(str);
|
|
334
|
+
break;
|
|
335
|
+
|
|
336
|
+
case SanitizationRules.HTML_STRICT:
|
|
337
|
+
result = this._stripHtml(str);
|
|
338
|
+
break;
|
|
339
|
+
|
|
340
|
+
case SanitizationRules.XSS:
|
|
341
|
+
result = this._sanitizeXss(str);
|
|
342
|
+
break;
|
|
343
|
+
|
|
344
|
+
case SanitizationRules.SQL:
|
|
345
|
+
result = this._sanitizeSql(str);
|
|
346
|
+
break;
|
|
347
|
+
|
|
348
|
+
case SanitizationRules.COMMAND:
|
|
349
|
+
result = this._sanitizeCommand(str);
|
|
350
|
+
break;
|
|
351
|
+
|
|
352
|
+
case SanitizationRules.PATH:
|
|
353
|
+
result = this._sanitizePath(str);
|
|
354
|
+
break;
|
|
355
|
+
|
|
356
|
+
case SanitizationRules.URL:
|
|
357
|
+
result = this._validateAndSanitizeUrl(str);
|
|
358
|
+
break;
|
|
359
|
+
|
|
360
|
+
case SanitizationRules.EMAIL:
|
|
361
|
+
result = this._validateEmail(str);
|
|
362
|
+
break;
|
|
363
|
+
|
|
364
|
+
case SanitizationRules.ALPHANUMERIC:
|
|
365
|
+
result = this._validateAlphanumeric(str);
|
|
366
|
+
break;
|
|
367
|
+
|
|
368
|
+
case SanitizationRules.UUID:
|
|
369
|
+
result = this._validateUuid(str);
|
|
370
|
+
break;
|
|
371
|
+
|
|
372
|
+
case SanitizationRules.INTEGER:
|
|
373
|
+
result = this._validateInteger(str);
|
|
374
|
+
break;
|
|
375
|
+
|
|
376
|
+
case SanitizationRules.FLOAT:
|
|
377
|
+
result = this._validateFloat(str);
|
|
378
|
+
break;
|
|
379
|
+
|
|
380
|
+
case SanitizationRules.BOOLEAN:
|
|
381
|
+
result = this._validateBoolean(str);
|
|
382
|
+
break;
|
|
383
|
+
|
|
384
|
+
case SanitizationRules.JSON:
|
|
385
|
+
result = this._validateJson(str);
|
|
386
|
+
break;
|
|
387
|
+
|
|
388
|
+
case SanitizationRules.BASE64:
|
|
389
|
+
result = this._validateBase64(str);
|
|
390
|
+
break;
|
|
391
|
+
|
|
392
|
+
case SanitizationRules.HEX:
|
|
393
|
+
result = this._validateHex(str);
|
|
394
|
+
break;
|
|
395
|
+
|
|
396
|
+
case SanitizationRules.SLUG:
|
|
397
|
+
result = this._validateSlug(str);
|
|
398
|
+
break;
|
|
399
|
+
|
|
400
|
+
case SanitizationRules.DOMAIN:
|
|
401
|
+
result = this._validateDomain(str);
|
|
402
|
+
break;
|
|
403
|
+
|
|
404
|
+
case SanitizationRules.IPV4:
|
|
405
|
+
result = this._validateIpv4(str);
|
|
406
|
+
break;
|
|
407
|
+
|
|
408
|
+
case SanitizationRules.IPV6:
|
|
409
|
+
result = this._validateIpv6(str);
|
|
410
|
+
break;
|
|
411
|
+
|
|
412
|
+
default:
|
|
413
|
+
// Check custom rules
|
|
414
|
+
if (this.options.customRules[rule]) {
|
|
415
|
+
result = this.options.customRules[rule](str);
|
|
416
|
+
} else {
|
|
417
|
+
result = this._escapeHtml(str); // Default to HTML escape
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Cache result
|
|
422
|
+
if (this.options.enableCache) {
|
|
423
|
+
const cacheKey = `${rule}:${str}`;
|
|
424
|
+
this._addToCache(cacheKey, result);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Sanitize array
|
|
432
|
+
* @private
|
|
433
|
+
*/
|
|
434
|
+
_sanitizeArray(arr, rules) {
|
|
435
|
+
return arr.map((item) => this.sanitize(item, rules));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Sanitize object recursively
|
|
440
|
+
* @private
|
|
441
|
+
*/
|
|
442
|
+
_sanitizeObject(obj, rules, depth = 0) {
|
|
443
|
+
if (depth >= this.options.defaultObjectDepth) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
`Maximum object depth exceeded: ${this.options.defaultObjectDepth}`,
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const sanitized = {};
|
|
450
|
+
|
|
451
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
452
|
+
// Sanitize key
|
|
453
|
+
const safeKey = this._sanitizeString(key, SanitizationRules.ALPHANUMERIC);
|
|
454
|
+
|
|
455
|
+
// Determine rule for this field
|
|
456
|
+
const fieldRule = rules && typeof rules === 'object' ? rules[key] : rules;
|
|
457
|
+
|
|
458
|
+
// Sanitize value
|
|
459
|
+
if (typeof value === 'string') {
|
|
460
|
+
sanitized[safeKey] = this._sanitizeString(value, fieldRule);
|
|
461
|
+
} else if (Array.isArray(value)) {
|
|
462
|
+
sanitized[safeKey] = this._sanitizeArray(value, fieldRule);
|
|
463
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
464
|
+
sanitized[safeKey] = this._sanitizeObject(value, fieldRule, depth + 1);
|
|
465
|
+
} else {
|
|
466
|
+
sanitized[safeKey] = value;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return sanitized;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Escape HTML entities
|
|
475
|
+
* @private
|
|
476
|
+
*/
|
|
477
|
+
_escapeHtml(str) {
|
|
478
|
+
return str.replace(/[&<>"'/]/g, (char) => HtmlEntityMap[char] || char);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Strip all HTML tags
|
|
483
|
+
* @private
|
|
484
|
+
*/
|
|
485
|
+
_stripHtml(str) {
|
|
486
|
+
return str.replace(/<[^>]*>/g, '');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Aggressive XSS sanitization
|
|
491
|
+
* @private
|
|
492
|
+
*/
|
|
493
|
+
_sanitizeXss(str) {
|
|
494
|
+
let sanitized = str;
|
|
495
|
+
|
|
496
|
+
// Apply all XSS patterns
|
|
497
|
+
for (const pattern of DangerousPatterns.xss) {
|
|
498
|
+
if (pattern.test(sanitized)) {
|
|
499
|
+
this.stats.blocked++;
|
|
500
|
+
sanitized = sanitized.replace(pattern, '');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// HTML escape remaining content
|
|
505
|
+
sanitized = this._escapeHtml(sanitized);
|
|
506
|
+
|
|
507
|
+
return sanitized;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* SQL injection sanitization
|
|
512
|
+
* @private
|
|
513
|
+
*/
|
|
514
|
+
_sanitizeSql(str) {
|
|
515
|
+
let sanitized = str;
|
|
516
|
+
|
|
517
|
+
// Check for SQL injection patterns
|
|
518
|
+
for (const pattern of DangerousPatterns.sql) {
|
|
519
|
+
if (pattern.test(sanitized)) {
|
|
520
|
+
this.stats.blocked++;
|
|
521
|
+
if (this.options.throwOnInvalid) {
|
|
522
|
+
throw new Error('Potential SQL injection detected');
|
|
523
|
+
}
|
|
524
|
+
// Remove dangerous patterns
|
|
525
|
+
sanitized = sanitized.replace(pattern, '');
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Escape single quotes (basic SQL escaping)
|
|
530
|
+
sanitized = sanitized.replace(/'/g, '\'\'');
|
|
531
|
+
|
|
532
|
+
return sanitized;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Command injection sanitization
|
|
537
|
+
* @private
|
|
538
|
+
*/
|
|
539
|
+
_sanitizeCommand(str) {
|
|
540
|
+
// Check for command injection patterns
|
|
541
|
+
for (const pattern of DangerousPatterns.command) {
|
|
542
|
+
if (pattern.test(str)) {
|
|
543
|
+
this.stats.blocked++;
|
|
544
|
+
if (this.options.throwOnInvalid) {
|
|
545
|
+
throw new Error('Potential command injection detected');
|
|
546
|
+
}
|
|
547
|
+
return '';
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return str;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Path traversal sanitization
|
|
556
|
+
* @private
|
|
557
|
+
*/
|
|
558
|
+
_sanitizePath(str) {
|
|
559
|
+
// Iteratively decode URL encoding to catch double/triple encoded attacks
|
|
560
|
+
// e.g., %252e%252e%252f -> %2e%2e%2f -> ../
|
|
561
|
+
let current = str;
|
|
562
|
+
let previous = '';
|
|
563
|
+
let iterations = 0;
|
|
564
|
+
const MAX_DECODE_ITERATIONS = 5;
|
|
565
|
+
|
|
566
|
+
while (current !== previous && iterations < MAX_DECODE_ITERATIONS) {
|
|
567
|
+
previous = current;
|
|
568
|
+
try {
|
|
569
|
+
current = decodeURIComponent(current);
|
|
570
|
+
} catch (e) {
|
|
571
|
+
// Malformed URL encoding might indicate attack - log and block
|
|
572
|
+
this.stats.blocked++;
|
|
573
|
+
throw new Error(
|
|
574
|
+
'Potential path traversal detected (malformed encoding)',
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
iterations++;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Check for path traversal patterns in both original and fully decoded versions
|
|
581
|
+
for (const pattern of DangerousPatterns.pathTraversal) {
|
|
582
|
+
if (pattern.test(str) || pattern.test(current)) {
|
|
583
|
+
this.stats.blocked++;
|
|
584
|
+
// SECURITY: Always throw on path traversal - it should never be silently sanitized
|
|
585
|
+
// Empty paths could resolve to application root, creating security vulnerabilities
|
|
586
|
+
throw new Error('Potential path traversal detected');
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Remove leading/trailing slashes
|
|
591
|
+
current = current.replace(/^[/\\]+|[/\\]+$/g, '');
|
|
592
|
+
|
|
593
|
+
// Normalize path separators
|
|
594
|
+
current = current.replace(/\\/g, '/');
|
|
595
|
+
|
|
596
|
+
return current;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* URL validation and sanitization
|
|
601
|
+
* @private
|
|
602
|
+
*/
|
|
603
|
+
_validateAndSanitizeUrl(str) {
|
|
604
|
+
// Check against URL pattern
|
|
605
|
+
if (!ValidationPatterns.url.test(str)) {
|
|
606
|
+
if (this.options.throwOnInvalid) {
|
|
607
|
+
throw new Error('Invalid URL format');
|
|
608
|
+
}
|
|
609
|
+
return '';
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Only allow http and https protocols
|
|
613
|
+
if (!str.startsWith('http://') && !str.startsWith('https://')) {
|
|
614
|
+
if (this.options.throwOnInvalid) {
|
|
615
|
+
throw new Error('URL must use http or https protocol');
|
|
616
|
+
}
|
|
617
|
+
return '';
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return str;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Email validation
|
|
625
|
+
* @private
|
|
626
|
+
*/
|
|
627
|
+
_validateEmail(str) {
|
|
628
|
+
if (!ValidationPatterns.email.test(str)) {
|
|
629
|
+
if (this.options.throwOnInvalid) {
|
|
630
|
+
throw new Error('Invalid email format');
|
|
631
|
+
}
|
|
632
|
+
return '';
|
|
633
|
+
}
|
|
634
|
+
return str.toLowerCase();
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Alphanumeric validation
|
|
639
|
+
* @private
|
|
640
|
+
*/
|
|
641
|
+
_validateAlphanumeric(str) {
|
|
642
|
+
if (!ValidationPatterns.alphanumeric.test(str)) {
|
|
643
|
+
if (this.options.throwOnInvalid) {
|
|
644
|
+
throw new Error('Must contain only alphanumeric characters');
|
|
645
|
+
}
|
|
646
|
+
return str.replace(/[^a-zA-Z0-9]/g, '');
|
|
647
|
+
}
|
|
648
|
+
return str;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* UUID validation
|
|
653
|
+
* @private
|
|
654
|
+
*/
|
|
655
|
+
_validateUuid(str) {
|
|
656
|
+
if (!ValidationPatterns.uuid.test(str)) {
|
|
657
|
+
if (this.options.throwOnInvalid) {
|
|
658
|
+
throw new Error('Invalid UUID format');
|
|
659
|
+
}
|
|
660
|
+
return '';
|
|
661
|
+
}
|
|
662
|
+
return str.toLowerCase();
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Integer validation
|
|
667
|
+
* @private
|
|
668
|
+
*/
|
|
669
|
+
_validateInteger(str) {
|
|
670
|
+
if (!ValidationPatterns.integer.test(str)) {
|
|
671
|
+
if (this.options.throwOnInvalid) {
|
|
672
|
+
throw new Error('Invalid integer format');
|
|
673
|
+
}
|
|
674
|
+
return '';
|
|
675
|
+
}
|
|
676
|
+
return str;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Float validation
|
|
681
|
+
* @private
|
|
682
|
+
*/
|
|
683
|
+
_validateFloat(str) {
|
|
684
|
+
if (!ValidationPatterns.float.test(str)) {
|
|
685
|
+
if (this.options.throwOnInvalid) {
|
|
686
|
+
throw new Error('Invalid float format');
|
|
687
|
+
}
|
|
688
|
+
return '';
|
|
689
|
+
}
|
|
690
|
+
return str;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Boolean validation
|
|
695
|
+
* @private
|
|
696
|
+
*/
|
|
697
|
+
_validateBoolean(str) {
|
|
698
|
+
const lower = str.toLowerCase();
|
|
699
|
+
if (lower === 'true' || lower === '1' || lower === 'yes') {
|
|
700
|
+
return 'true';
|
|
701
|
+
} else if (lower === 'false' || lower === '0' || lower === 'no') {
|
|
702
|
+
return 'false';
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (this.options.throwOnInvalid) {
|
|
706
|
+
throw new Error('Invalid boolean value');
|
|
707
|
+
}
|
|
708
|
+
return '';
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* JSON validation
|
|
713
|
+
* @private
|
|
714
|
+
*/
|
|
715
|
+
_validateJson(str) {
|
|
716
|
+
try {
|
|
717
|
+
JSON.parse(str);
|
|
718
|
+
return str;
|
|
719
|
+
} catch (error) {
|
|
720
|
+
if (this.options.throwOnInvalid) {
|
|
721
|
+
throw new Error('Invalid JSON format');
|
|
722
|
+
}
|
|
723
|
+
return '';
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Base64 validation
|
|
729
|
+
* @private
|
|
730
|
+
*/
|
|
731
|
+
_validateBase64(str) {
|
|
732
|
+
if (!ValidationPatterns.base64.test(str)) {
|
|
733
|
+
if (this.options.throwOnInvalid) {
|
|
734
|
+
throw new Error('Invalid base64 format');
|
|
735
|
+
}
|
|
736
|
+
return '';
|
|
737
|
+
}
|
|
738
|
+
return str;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Hexadecimal validation
|
|
743
|
+
* @private
|
|
744
|
+
*/
|
|
745
|
+
_validateHex(str) {
|
|
746
|
+
if (!ValidationPatterns.hex.test(str)) {
|
|
747
|
+
if (this.options.throwOnInvalid) {
|
|
748
|
+
throw new Error('Invalid hexadecimal format');
|
|
749
|
+
}
|
|
750
|
+
return '';
|
|
751
|
+
}
|
|
752
|
+
return str.toLowerCase();
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Slug validation
|
|
757
|
+
* @private
|
|
758
|
+
*/
|
|
759
|
+
_validateSlug(str) {
|
|
760
|
+
if (!ValidationPatterns.slug.test(str)) {
|
|
761
|
+
if (this.options.throwOnInvalid) {
|
|
762
|
+
throw new Error('Invalid slug format');
|
|
763
|
+
}
|
|
764
|
+
// Convert to slug
|
|
765
|
+
return str
|
|
766
|
+
.toLowerCase()
|
|
767
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
768
|
+
.replace(/^-+|-+$/g, '');
|
|
769
|
+
}
|
|
770
|
+
return str;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Domain validation
|
|
775
|
+
* @private
|
|
776
|
+
*/
|
|
777
|
+
_validateDomain(str) {
|
|
778
|
+
if (!ValidationPatterns.domain.test(str)) {
|
|
779
|
+
if (this.options.throwOnInvalid) {
|
|
780
|
+
throw new Error('Invalid domain format');
|
|
781
|
+
}
|
|
782
|
+
return '';
|
|
783
|
+
}
|
|
784
|
+
return str.toLowerCase();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* IPv4 validation
|
|
789
|
+
* @private
|
|
790
|
+
*/
|
|
791
|
+
_validateIpv4(str) {
|
|
792
|
+
if (!ValidationPatterns.ipv4.test(str)) {
|
|
793
|
+
if (this.options.throwOnInvalid) {
|
|
794
|
+
throw new Error('Invalid IPv4 address');
|
|
795
|
+
}
|
|
796
|
+
return '';
|
|
797
|
+
}
|
|
798
|
+
return str;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* IPv6 validation
|
|
803
|
+
* @private
|
|
804
|
+
*/
|
|
805
|
+
_validateIpv6(str) {
|
|
806
|
+
if (!ValidationPatterns.ipv6.test(str)) {
|
|
807
|
+
if (this.options.throwOnInvalid) {
|
|
808
|
+
throw new Error('Invalid IPv6 address');
|
|
809
|
+
}
|
|
810
|
+
return '';
|
|
811
|
+
}
|
|
812
|
+
return str.toLowerCase();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Add entry to cache
|
|
817
|
+
* @private
|
|
818
|
+
*/
|
|
819
|
+
_addToCache(key, value) {
|
|
820
|
+
// Enforce cache size limit
|
|
821
|
+
if (this.cache.size >= this.options.cacheSize) {
|
|
822
|
+
// Remove oldest entry
|
|
823
|
+
const oldestKey = Array.from(this.cacheAccessTimes.entries()).sort(
|
|
824
|
+
([, a], [, b]) => a - b,
|
|
825
|
+
)[0][0];
|
|
826
|
+
this.cache.delete(oldestKey);
|
|
827
|
+
this.cacheAccessTimes.delete(oldestKey);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
this.cache.set(key, value);
|
|
831
|
+
this.cacheAccessTimes.set(key, Date.now());
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Start cache cleanup interval
|
|
836
|
+
* @private
|
|
837
|
+
*/
|
|
838
|
+
_startCacheCleanup() {
|
|
839
|
+
this.cacheCleanupInterval = setInterval(() => {
|
|
840
|
+
const now = Date.now();
|
|
841
|
+
const expired = [];
|
|
842
|
+
|
|
843
|
+
for (const [key, accessTime] of this.cacheAccessTimes.entries()) {
|
|
844
|
+
if (now - accessTime > this.options.cacheTTL) {
|
|
845
|
+
expired.push(key);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
for (const key of expired) {
|
|
850
|
+
this.cache.delete(key);
|
|
851
|
+
this.cacheAccessTimes.delete(key);
|
|
852
|
+
}
|
|
853
|
+
}, this.options.cacheTTL);
|
|
854
|
+
|
|
855
|
+
// Don't block process exit
|
|
856
|
+
if (this.cacheCleanupInterval.unref) {
|
|
857
|
+
this.cacheCleanupInterval.unref();
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Get sanitizer statistics
|
|
863
|
+
*
|
|
864
|
+
* @returns {Object} Statistics object
|
|
865
|
+
*/
|
|
866
|
+
getStats() {
|
|
867
|
+
return {
|
|
868
|
+
...this.stats,
|
|
869
|
+
cacheSize: this.cache.size,
|
|
870
|
+
cacheHitRate: this.stats.cached / Math.max(1, this.stats.sanitized),
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Reset statistics
|
|
876
|
+
*/
|
|
877
|
+
resetStats() {
|
|
878
|
+
this.stats = {
|
|
879
|
+
sanitized: 0,
|
|
880
|
+
cached: 0,
|
|
881
|
+
blocked: 0,
|
|
882
|
+
validationErrors: 0,
|
|
883
|
+
averageTime: 0,
|
|
884
|
+
totalTime: 0,
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Clear cache
|
|
890
|
+
*/
|
|
891
|
+
clearCache() {
|
|
892
|
+
this.cache.clear();
|
|
893
|
+
this.cacheAccessTimes.clear();
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Destroy sanitizer and cleanup resources
|
|
898
|
+
*/
|
|
899
|
+
destroy() {
|
|
900
|
+
if (this.cacheCleanupInterval) {
|
|
901
|
+
clearInterval(this.cacheCleanupInterval);
|
|
902
|
+
this.cacheCleanupInterval = null;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
this.clearCache();
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Singleton instance with default configuration
|
|
911
|
+
*/
|
|
912
|
+
const defaultSanitizer = new InputSanitizer();
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Quick sanitization functions (using singleton)
|
|
916
|
+
*/
|
|
917
|
+
function sanitizeInput(input, rules = null) {
|
|
918
|
+
return defaultSanitizer.sanitize(input, rules);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function sanitizeHtml(str) {
|
|
922
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.HTML);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function sanitizeXss(str) {
|
|
926
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.XSS);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function sanitizeSql(str) {
|
|
930
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.SQL);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function sanitizeCommand(str) {
|
|
934
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.COMMAND);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function sanitizePath(str) {
|
|
938
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.PATH);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function validateEmail(str) {
|
|
942
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.EMAIL);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function validateUrl(str) {
|
|
946
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.URL);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
function validateUuid(str) {
|
|
950
|
+
return defaultSanitizer.sanitize(str, SanitizationRules.UUID);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
module.exports = {
|
|
954
|
+
InputSanitizer,
|
|
955
|
+
SanitizationRules,
|
|
956
|
+
ValidationPatterns,
|
|
957
|
+
sanitizeInput,
|
|
958
|
+
sanitizeHtml,
|
|
959
|
+
sanitizeXss,
|
|
960
|
+
sanitizeSql,
|
|
961
|
+
sanitizeCommand,
|
|
962
|
+
sanitizePath,
|
|
963
|
+
validateEmail,
|
|
964
|
+
validateUrl,
|
|
965
|
+
validateUuid,
|
|
966
|
+
defaultSanitizer,
|
|
967
|
+
};
|