Spaces:
Running
Running
File size: 4,406 Bytes
0f3460d | 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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | """
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
|