fix: resolve incomplete multi-character sanitization in sanitizeMessage

Use stable replacement loop for all multi-character patterns to prevent malicious input from reappearing after sanitization.
This commit is contained in:
lockbitchat
2025-10-20 01:20:02 -04:00
parent 7604381584
commit 0d7835cfa2
3 changed files with 49 additions and 31 deletions
+19 -3
View File
@@ -2760,6 +2760,14 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
if (typeof message !== "string") { if (typeof message !== "string") {
throw new Error("Message must be a string"); throw new Error("Message must be a string");
} }
function replaceUntilStable(str, pattern, replacement = "") {
let previous;
do {
previous = str;
str = str.replace(pattern, replacement);
} while (str !== previous);
return str;
}
const dangerousPatterns = [ const dangerousPatterns = [
// Script tags with various formats // Script tags with various formats
/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi, /<script\b[^>]*>[\s\S]*?<\/script\s*>/gi,
@@ -2793,12 +2801,20 @@ var EnhancedSecureCryptoUtils = class _EnhancedSecureCryptoUtils {
do { do {
previousLength = sanitized.length; previousLength = sanitized.length;
for (const pattern of dangerousPatterns) { for (const pattern of dangerousPatterns) {
sanitized = sanitized.replace(pattern, ""); sanitized = replaceUntilStable(sanitized, pattern);
} }
sanitized = sanitized.replace(/<[^>]*>/g, "").replace(/^\w+:/gi, "").replace(/\bon\w+\s*=\s*["'][^"']*["']/gi, "").replace(/\bon\w+\s*=\s*[^>\s]+/gi, "").replace(/[<>]/g, "").trim(); sanitized = replaceUntilStable(sanitized, /<[^>]*>/g);
sanitized = replaceUntilStable(sanitized, /^\w+:/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*["'][^"']*["']/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*[^>\s]+/gi);
sanitized = sanitized.replace(/[<>]/g, "").trim();
iterations++; iterations++;
} while (sanitized.length !== previousLength && iterations < maxIterations); } while (sanitized.length !== previousLength && iterations < maxIterations);
sanitized = sanitized.replace(/<[^>]*>/g, "").replace(/^\w+:/gi, "").replace(/\bon\w+\s*=\s*["'][^"']*["']/gi, "").replace(/\bon\w+\s*=\s*[^>\s]+/gi, "").replace(/[<>]/g, "").trim(); sanitized = replaceUntilStable(sanitized, /<[^>]*>/g);
sanitized = replaceUntilStable(sanitized, /^\w+:/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*["'][^"']*["']/gi);
sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*[^>\s]+/gi);
sanitized = sanitized.replace(/[<>]/g, "").trim();
return sanitized.substring(0, 2e3); return sanitized.substring(0, 2e3);
} }
// Generate cryptographically secure salt (64 bytes for enhanced security) // Generate cryptographically secure salt (64 bytes for enhanced security)
+2 -2
View File
File diff suppressed because one or more lines are too long
+28 -26
View File
@@ -2529,6 +2529,16 @@ class EnhancedSecureCryptoUtils {
throw new Error('Message must be a string'); throw new Error('Message must be a string');
} }
// Helper function to apply replacement until stable
function replaceUntilStable(str, pattern, replacement = '') {
let previous;
do {
previous = str;
str = str.replace(pattern, replacement);
} while (str !== previous);
return str;
}
// Define all dangerous patterns that need to be removed // Define all dangerous patterns that need to be removed
const dangerousPatterns = [ const dangerousPatterns = [
// Script tags with various formats // Script tags with various formats
@@ -2566,39 +2576,31 @@ class EnhancedSecureCryptoUtils {
do { do {
previousLength = sanitized.length; previousLength = sanitized.length;
// Apply all dangerous patterns // Apply all dangerous patterns with stable replacement
for (const pattern of dangerousPatterns) { for (const pattern of dangerousPatterns) {
sanitized = sanitized.replace(pattern, ''); sanitized = replaceUntilStable(sanitized, pattern);
} }
// Additional cleanup for edge cases // Additional cleanup for edge cases - each applied until stable
sanitized = sanitized sanitized = replaceUntilStable(sanitized, /<[^>]*>/g);
// Remove any remaining angle brackets that might form tags sanitized = replaceUntilStable(sanitized, /^\w+:/gi);
.replace(/<[^>]*>/g, '') sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*["'][^"']*["']/gi);
// Remove any remaining protocol handlers sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*[^>\s]+/gi);
.replace(/^\w+:/gi, '')
// Remove any remaining event handlers // Single character removal is inherently safe
.replace(/\bon\w+\s*=\s*["'][^"']*["']/gi, '') sanitized = sanitized.replace(/[<>]/g, '').trim();
.replace(/\bon\w+\s*=\s*[^>\s]+/gi, '')
// Remove any remaining dangerous characters
.replace(/[<>]/g, '')
.trim();
iterations++; iterations++;
} while (sanitized.length !== previousLength && iterations < maxIterations); } while (sanitized.length !== previousLength && iterations < maxIterations);
// Final security pass: remove any remaining potential XSS vectors // Final security pass with stable replacements
sanitized = sanitized sanitized = replaceUntilStable(sanitized, /<[^>]*>/g);
// Remove any remaining HTML-like content sanitized = replaceUntilStable(sanitized, /^\w+:/gi);
.replace(/<[^>]*>/g, '') sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*["'][^"']*["']/gi);
// Remove any remaining protocol handlers sanitized = replaceUntilStable(sanitized, /\bon\w+\s*=\s*[^>\s]+/gi);
.replace(/^\w+:/gi, '')
// Remove any remaining event handlers // Final single character cleanup
.replace(/\bon\w+\s*=\s*["'][^"']*["']/gi, '') sanitized = sanitized.replace(/[<>]/g, '').trim();
.replace(/\bon\w+\s*=\s*[^>\s]+/gi, '')
// Remove any remaining dangerous characters
.replace(/[<>]/g, '')
.trim();
return sanitized.substring(0, 2000); // Limit length return sanitized.substring(0, 2000); // Limit length
} }