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:
2025-12-02 01:29:35 +00:00
parent 2ce89e7c6c
commit 534796edd7
9 changed files with 984 additions and 16 deletions

View File

@@ -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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;')
.replace(/\//g, '&#x2F;')
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' }