promptpulse
Version:
Track and analyze your Claude Code usage across multiple machines with team collaboration
240 lines (195 loc) • 6.92 kB
JavaScript
// Server-side security utilities for input sanitization and SQL injection prevention
/**
* Escapes SQL special characters to prevent SQL injection
* @param {string} input - The input string to escape
* @returns {string} - The escaped string
*/
export function escapeSqlCharacters(input) {
if (!input || typeof input !== "string") return "";
return input
.replace(/'/g, "''") // Escape single quotes
.replace(/\\/g, "\\\\") // Escape backslashes
.replace(/\0/g, "\\0") // Escape null bytes
.replace(/\n/g, "\\n") // Escape newlines
.replace(/\r/g, "\\r") // Escape carriage returns
.replace(/\x1a/g, "\\Z"); // Escape Control+Z
}
/**
* Sanitizes display names with SQL injection protection
* @param {string} input - The display name to sanitize
* @returns {string} - The sanitized display name
*/
export function sanitizeDisplayName(input) {
if (!input || typeof input !== "string") return "";
// Trim whitespace and limit length
let sanitized = input.trim().slice(0, 50);
// Remove HTML tags and dangerous characters
sanitized = sanitized.replace(/<[^>]*>/g, "");
sanitized = sanitized.replace(/[<>'"&]/g, "");
// SQL injection protection
sanitized = escapeSqlCharacters(sanitized);
return sanitized;
}
/**
* Sanitizes search input to prevent SQL injection and XSS
* @param {string} input - The search input to sanitize
* @returns {string} - The sanitized search input
*/
export function sanitizeSearchInput(input) {
if (!input || typeof input !== "string") return "";
// Trim and limit length
let sanitized = input.trim().slice(0, 100);
// Remove HTML tags
sanitized = sanitized.replace(/<[^>]*>/g, "");
// Remove dangerous SQL patterns (keywords that could be used in injections)
sanitized = sanitized.replace(/(\b(ALTER|CREATE|DELETE|DROP|EXEC|INSERT|MERGE|SELECT|UPDATE|UNION|INTO|FROM|WHERE|SCRIPT|JAVASCRIPT)\b)/gi, "");
// Escape SQL characters
sanitized = escapeSqlCharacters(sanitized);
return sanitized;
}
/**
* Generic input sanitization for general use
* @param {string} input - The input to sanitize
* @param {number} maxLength - Maximum allowed length (default: 255)
* @returns {string} - The sanitized input
*/
export function sanitizeGenericInput(input, maxLength = 255) {
if (!input || typeof input !== "string") return "";
// Trim and limit length
let sanitized = input.trim().slice(0, maxLength);
// Remove HTML tags and script-related content
sanitized = sanitized.replace(/<[^>]*>/g, "");
sanitized = sanitized.replace(/javascript:/gi, "");
sanitized = sanitized.replace(/on\w+\s*=/gi, "");
// Remove dangerous characters
sanitized = sanitized.replace(/[<>'"&]/g, "");
// SQL injection protection
sanitized = escapeSqlCharacters(sanitized);
return sanitized;
}
/**
* Validates SQL identifiers (table names, column names, etc.)
* @param {string} identifier - The identifier to validate
* @returns {boolean} - True if valid, false otherwise
*/
export function validateSqlIdentifier(identifier) {
if (!identifier || typeof identifier !== "string") return false;
// Only allow alphanumeric characters, underscores, and hyphens
return /^[a-zA-Z0-9_-]+$/.test(identifier);
}
/**
* Sanitizes machine ID input
* @param {string} input - The machine ID to sanitize
* @returns {string} - The sanitized machine ID
*/
export function sanitizeMachineId(input) {
if (!input || typeof input !== "string") return "";
// Trim and limit length
let sanitized = input.trim().slice(0, 100);
// Only allow alphanumeric, underscores, hyphens, and dots
sanitized = sanitized.replace(/[^a-zA-Z0-9_\-.]/g, "");
// SQL protection
sanitized = escapeSqlCharacters(sanitized);
return sanitized;
}
/**
* Sanitizes project path input
* @param {string} input - The project path to sanitize
* @returns {string} - The sanitized project path
*/
export function sanitizeProjectPath(input) {
if (!input || typeof input !== "string") return "";
// Trim and limit length
let sanitized = input.trim().slice(0, 500);
// Remove dangerous characters but allow path separators
sanitized = sanitized.replace(/[<>'"&]/g, "");
// SQL injection protection
sanitized = escapeSqlCharacters(sanitized);
return sanitized;
}
/**
* Validates and sanitizes date strings
* @param {string} input - The date string to validate
* @returns {string|null} - The sanitized date string or null if invalid
*/
export function sanitizeDateString(input) {
if (!input || typeof input !== "string") return null;
// Basic ISO date format validation
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(input)) return null;
// Try to parse as date to ensure validity
const date = new Date(input);
if (isNaN(date.getTime())) return null;
return input;
}
/**
* Detects potential SQL injection patterns in input
* @param {string} input - The input to analyze
* @returns {boolean} - True if potential injection detected
*/
export function detectSqlInjection(input) {
if (!input || typeof input !== "string") return false;
const suspiciousPatterns = [
/(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)/gi,
/(\b(OR|AND)\s+\d+\s*=\s*\d+)/gi,
/'/g,
/--/g,
/\/\*/g,
/\*\//g,
/;/g
];
return suspiciousPatterns.some(pattern => pattern.test(input));
}
/**
* Comprehensive input validation and sanitization
* @param {string} input - The input to process
* @param {Object} options - Validation options
* @returns {Object} - Object with sanitized input and validation results
*/
export function validateAndSanitize(input, options = {}) {
const {
maxLength = 255,
checkSqlInjection = true,
type = "generic"
} = options;
const result = {
original: input,
sanitized: "",
isValid: true,
warnings: []
};
if (!input || typeof input !== "string") {
result.isValid = false;
result.warnings.push("Invalid input type");
return result;
}
// Check for SQL injection patterns
if (checkSqlInjection && detectSqlInjection(input)) {
result.warnings.push("Potential SQL injection detected");
}
// Apply appropriate sanitization based on type
switch (type) {
case "displayName":
result.sanitized = sanitizeDisplayName(input);
break;
case "search":
result.sanitized = sanitizeSearchInput(input);
break;
case "machineId":
result.sanitized = sanitizeMachineId(input);
break;
case "projectPath":
result.sanitized = sanitizeProjectPath(input);
break;
case "date":
result.sanitized = sanitizeDateString(input);
if (!result.sanitized) {
result.isValid = false;
result.warnings.push("Invalid date format");
}
break;
default:
result.sanitized = sanitizeGenericInput(input, maxLength);
}
return result;
}