Spaces:
Running
Running
File size: 2,982 Bytes
bd161ec |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
"""
Security utilities for the application.
"""
import secrets
import string
from typing import Optional
import re
def generate_secure_token(length: int = 32) -> str:
"""Generate a secure random token."""
alphabet = string.ascii_letters + string.digits
return ''.join(secrets.choice(alphabet) for _ in range(length))
def validate_email(email: str) -> bool:
"""Validate email format."""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
def validate_password_strength(password: str) -> tuple[bool, str]:
"""
Validate password strength.
Returns (is_valid, message)
"""
if len(password) < 8:
return False, "Password must be at least 8 characters long"
if not re.search(r'[A-Z]', password):
return False, "Password must contain at least one uppercase letter"
if not re.search(r'[a-z]', password):
return False, "Password must contain at least one lowercase letter"
if not re.search(r'\d', password):
return False, "Password must contain at least one digit"
# Check for special characters
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
return False, "Password must contain at least one special character"
return True, "Password is strong"
def sanitize_filename(filename: str) -> str:
"""Sanitize filename for safe storage."""
# Remove path separators and dangerous characters
sanitized = re.sub(r'[^\w\-_.]', '_', filename)
# Limit length
if len(sanitized) > 255:
name, ext = sanitized.rsplit('.', 1) if '.' in sanitized else (sanitized, '')
max_name_length = 255 - len(ext) - 1 if ext else 255
sanitized = name[:max_name_length] + ('.' + ext if ext else '')
return sanitized
def validate_uuid(uuid_string: str) -> bool:
"""Validate UUID format."""
uuid_pattern = r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
return re.match(uuid_pattern, uuid_string, re.IGNORECASE) is not None
class RateLimiter:
"""Simple in-memory rate limiter."""
def __init__(self):
self.requests = {}
def is_allowed(self, key: str, max_requests: int, window_seconds: int) -> bool:
"""Check if request is allowed under rate limit."""
import time
current_time = time.time()
if key not in self.requests:
self.requests[key] = []
# Remove old requests outside the window
self.requests[key] = [
req_time for req_time in self.requests[key]
if current_time - req_time < window_seconds
]
# Check if under limit
if len(self.requests[key]) >= max_requests:
return False
# Add current request
self.requests[key].append(current_time)
return True
# Global rate limiter instance
rate_limiter = RateLimiter()
|