feat: implement security enhancements (task 17)
- Add rate limiting for login, registration, and invitations - Implement CSRF protection with SameSite cookies and security headers - Configure secure session cookies with httpOnly and secure flags - Enhance input sanitization for XSS and injection prevention - Add comprehensive security tests (41 tests passing) - Document security enhancements in SECURITY_ENHANCEMENTS.md Requirements: 9.3, 9.4, 9.5
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Input validation utilities for the Ultimate Lists App
|
||||
* Provides validation functions and sanitization for user inputs
|
||||
*
|
||||
* Requirements: 9.5 - Input sanitization for all user inputs
|
||||
*/
|
||||
|
||||
export interface ValidationResult {
|
||||
@@ -9,13 +11,45 @@ export interface ValidationResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize string input by trimming whitespace
|
||||
* Sanitize string input by trimming whitespace and removing potentially dangerous characters
|
||||
* Requirement 9.5 - Input sanitization
|
||||
*/
|
||||
export function sanitizeString(input: string | null | undefined): string {
|
||||
if (input === null || input === undefined) {
|
||||
return ''
|
||||
}
|
||||
return input.trim()
|
||||
|
||||
// Trim whitespace
|
||||
let sanitized = input.trim()
|
||||
|
||||
// Remove null bytes that could cause issues
|
||||
sanitized = sanitized.replace(/\0/g, '')
|
||||
|
||||
// Remove control characters except newlines and tabs
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize email input
|
||||
* Requirement 9.5 - Input sanitization
|
||||
*/
|
||||
export function sanitizeEmail(email: string | null | undefined): string {
|
||||
if (email === null || email === undefined) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// Convert to lowercase and trim
|
||||
let sanitized = email.toLowerCase().trim()
|
||||
|
||||
// Remove any whitespace within the email
|
||||
sanitized = sanitized.replace(/\s/g, '')
|
||||
|
||||
// Remove null bytes and control characters
|
||||
sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, '')
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,10 +216,59 @@ export function validateNotes(notes: string | null | undefined): ValidationResul
|
||||
|
||||
/**
|
||||
* Sanitize HTML to prevent XSS attacks
|
||||
* Basic implementation - strips HTML tags
|
||||
* Strips HTML tags and dangerous characters
|
||||
* Requirement 9.5 - Input sanitization to prevent XSS
|
||||
*/
|
||||
export function sanitizeHtml(input: string): string {
|
||||
return input.replace(/<[^>]*>/g, '')
|
||||
if (!input) return ''
|
||||
|
||||
let sanitized = input
|
||||
|
||||
// First, remove script tags and their content (before removing other tags)
|
||||
sanitized = sanitized.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
||||
|
||||
// Remove event handlers (onclick, onerror, etc.)
|
||||
sanitized = sanitized.replace(/on\w+\s*=\s*["'][^"']*["']/gi, '')
|
||||
sanitized = sanitized.replace(/on\w+\s*=\s*[^\s>]*/gi, '')
|
||||
|
||||
// Remove javascript: protocol
|
||||
sanitized = sanitized.replace(/javascript:/gi, '')
|
||||
|
||||
// Remove data: protocol (can be used for XSS)
|
||||
sanitized = sanitized.replace(/data:text\/html/gi, '')
|
||||
|
||||
// Remove all HTML tags
|
||||
sanitized = sanitized.replace(/<[^>]*>/g, '')
|
||||
|
||||
// Encode special HTML characters (after removing tags)
|
||||
sanitized = sanitized
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\//g, '/')
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize SQL input to prevent SQL injection
|
||||
* Note: This is a defense-in-depth measure. Primary protection comes from parameterized queries.
|
||||
* Requirement 9.5 - Input sanitization
|
||||
*/
|
||||
export function sanitizeSqlInput(input: string): string {
|
||||
if (!input) return ''
|
||||
|
||||
// Remove SQL comment markers
|
||||
let sanitized = input.replace(/--/g, '')
|
||||
sanitized = sanitized.replace(/\/\*/g, '')
|
||||
sanitized = sanitized.replace(/\*\//g, '')
|
||||
|
||||
// Remove semicolons that could terminate statements
|
||||
sanitized = sanitized.replace(/;/g, '')
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,9 +387,10 @@ export function validateItemData(data: {
|
||||
|
||||
/**
|
||||
* Validate email format
|
||||
* Requirement 9.5 - Input validation and sanitization
|
||||
*/
|
||||
export function validateEmail(email: string): ValidationResult {
|
||||
const sanitized = sanitizeString(email)
|
||||
const sanitized = sanitizeEmail(email)
|
||||
|
||||
if (!sanitized) {
|
||||
return { isValid: false, error: 'Email is required' }
|
||||
|
||||
Reference in New Issue
Block a user