Spaces:
Running
Running
| """ | |
| Input Validation Utilities | |
| μ λ ₯κ° κ²μ¦ λ° sanitization | |
| SQL Injection λ°©μ§λ₯Ό μν μΆκ° κ²μ¦ λ μ΄μ΄ | |
| """ | |
| import re | |
| from typing import Any, List, Optional | |
| from fastapi import HTTPException | |
| # UUID v4 μ κ·μ | |
| UUID_PATTERN = re.compile( | |
| r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', | |
| re.IGNORECASE | |
| ) | |
| def validate_uuid(value: str, field_name: str = "id") -> str: | |
| """ | |
| UUID νμ κ²μ¦ | |
| Args: | |
| value: κ²μ¦ν κ° | |
| field_name: νλ μ΄λ¦ (μλ¬ λ©μμ§μ©) | |
| Returns: | |
| κ²μ¦λ UUID λ¬Έμμ΄ | |
| Raises: | |
| HTTPException: UUID νμμ΄ μλ κ²½μ° | |
| """ | |
| if not value or not isinstance(value, str): | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"{field_name} must be a valid UUID" | |
| ) | |
| value = value.strip().lower() | |
| if not UUID_PATTERN.match(value): | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"{field_name} must be a valid UUID format" | |
| ) | |
| return value | |
| def validate_user_id(user_id: str) -> str: | |
| """ | |
| μ¬μ©μ ID κ²μ¦ (UUID νμ) | |
| Args: | |
| user_id: μ¬μ©μ ID | |
| Returns: | |
| κ²μ¦λ user_id | |
| """ | |
| return validate_uuid(user_id, "user_id") | |
| def validate_ids(ids: List[str], field_name: str = "ids") -> List[str]: | |
| """ | |
| μ¬λ¬ UUID κ²μ¦ | |
| Args: | |
| ids: UUID 리μ€νΈ | |
| field_name: νλ μ΄λ¦ | |
| Returns: | |
| κ²μ¦λ UUID 리μ€νΈ | |
| """ | |
| if not ids or not isinstance(ids, list): | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"{field_name} must be a non-empty list" | |
| ) | |
| if len(ids) > 100: | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"{field_name} list too large (max 100)" | |
| ) | |
| return [validate_uuid(id_val, field_name) for id_val in ids] | |
| def sanitize_string(value: str, max_length: int = 1000) -> str: | |
| """ | |
| λ¬Έμμ΄ sanitization | |
| Args: | |
| value: μ λ ₯ λ¬Έμμ΄ | |
| max_length: μ΅λ κΈΈμ΄ | |
| Returns: | |
| μ μ λ λ¬Έμμ΄ | |
| """ | |
| if not value or not isinstance(value, str): | |
| return "" | |
| # μλ€ κ³΅λ°± μ κ±° | |
| value = value.strip() | |
| # κΈΈμ΄ μ ν | |
| if len(value) > max_length: | |
| value = value[:max_length] | |
| # μ μ΄ λ¬Έμ μ κ±° (μ€λ°κΏ, ν μ μΈ) | |
| value = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '', value) | |
| return value | |
| def validate_pagination( | |
| limit: int, | |
| offset: int, | |
| max_limit: int = 100 | |
| ) -> tuple[int, int]: | |
| """ | |
| νμ΄μ§λ€μ΄μ νλΌλ―Έν° κ²μ¦ | |
| Args: | |
| limit: μ‘°ν κ°μ | |
| offset: μμ μμΉ | |
| max_limit: μ΅λ limit κ° | |
| Returns: | |
| (limit, offset) νν | |
| """ | |
| if limit < 1: | |
| limit = 10 | |
| if limit > max_limit: | |
| limit = max_limit | |
| if offset < 0: | |
| offset = 0 | |
| return limit, offset | |
| def validate_enum( | |
| value: str, | |
| allowed_values: List[str], | |
| field_name: str = "value" | |
| ) -> str: | |
| """ | |
| Enum κ° κ²μ¦ | |
| Args: | |
| value: κ²μ¦ν κ° | |
| allowed_values: νμ©λ κ° λͺ©λ‘ | |
| field_name: νλ μ΄λ¦ | |
| Returns: | |
| κ²μ¦λ κ° | |
| """ | |
| if not value or value not in allowed_values: | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"{field_name} must be one of: {', '.join(allowed_values)}" | |
| ) | |
| return value | |
| def validate_coordinates(lat: float, lng: float) -> tuple[float, float]: | |
| """ | |
| μ’ν μ ν¨μ± κ²μ¦ | |
| Args: | |
| lat: μλ | |
| lng: κ²½λ | |
| Returns: | |
| (lat, lng) νν | |
| """ | |
| if not isinstance(lat, (int, float)) or not isinstance(lng, (int, float)): | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Coordinates must be numeric" | |
| ) | |
| if not -90 <= lat <= 90: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Latitude must be between -90 and 90" | |
| ) | |
| if not -180 <= lng <= 180: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Longitude must be between -180 and 180" | |
| ) | |
| return lat, lng | |