teoat commited on
Commit
f121445
·
verified ·
1 Parent(s): fa7464f

Upload core/validation.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. core/validation.py +49 -42
core/validation.py CHANGED
@@ -25,16 +25,16 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
25
  # Maximum request size (10MB)
26
  MAX_REQUEST_SIZE = 10 * 1024 * 1024
27
 
28
- # Patterns for detecting potential attacks
29
- SQL_INJECTION_PATTERN = re.compile(
30
- r"(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION|SCRIPT|OR\s+1\s*=\s*1|--)\b)",
31
  re.IGNORECASE,
32
  )
33
 
34
- XSS_PATTERN = re.compile(r"(<script|javascript:|onerror=|onload=|<iframe|<embed|<object)", re.IGNORECASE)
35
-
36
  # Path traversal detection
37
- PATH_TRAVERSAL_PATTERN = re.compile(r"(\.\./|\.\.\\|%2e%2e%2f|%2e%2e/|\.\.%2f|%2e%2e%5c)", re.IGNORECASE)
 
 
38
 
39
  # Command injection detection
40
  COMMAND_INJECTION_PATTERN = re.compile(
@@ -79,7 +79,10 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
79
  if "multipart/form-data" in content_type:
80
  # Allow multipart for file uploads, will validate individual parts
81
  pass
82
- elif content_type not in self.ALLOWED_CONTENT_TYPES and content_type != "application/x-www-form-urlencoded":
 
 
 
83
  logger.warning(
84
  "Invalid content type",
85
  extra={
@@ -88,7 +91,9 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
88
  "client_ip": request.client.host if request.client else None,
89
  },
90
  )
91
- raise HTTPException(status_code=415, detail=f"Unsupported content type: {content_type}")
 
 
92
 
93
  # Get request body for POST/PUT/PATCH
94
  if request.method in ["POST", "PUT", "PATCH"]:
@@ -96,21 +101,6 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
96
  body = await request.body()
97
  body_str = body.decode("utf-8")
98
 
99
- # Check for SQL injection patterns
100
- if self.SQL_INJECTION_PATTERN.search(body_str):
101
- logger.error(
102
- "SQL injection attempt detected",
103
- extra={
104
- "path": str(request.url.path),
105
- "method": request.method,
106
- "client_ip": (request.client.host if request.client else None),
107
- },
108
- )
109
- raise HTTPException(
110
- status_code=400,
111
- detail="Invalid input: Potential SQL injection detected",
112
- )
113
-
114
  # Check for XSS patterns
115
  if self.XSS_PATTERN.search(body_str):
116
  logger.error(
@@ -118,10 +108,14 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
118
  extra={
119
  "path": str(request.url.path),
120
  "method": request.method,
121
- "client_ip": (request.client.host if request.client else None),
 
 
122
  },
123
  )
124
- raise HTTPException(status_code=400, detail="Invalid input: Potential XSS detected")
 
 
125
 
126
  # Check for path traversal
127
  if self.PATH_TRAVERSAL_PATTERN.search(body_str):
@@ -130,10 +124,14 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
130
  extra={
131
  "path": str(request.url.path),
132
  "method": request.method,
133
- "client_ip": (request.client.host if request.client else None),
 
 
134
  },
135
  )
136
- raise HTTPException(status_code=400, detail="Invalid input: Path traversal detected")
 
 
137
 
138
  # Check for command injection
139
  if self.COMMAND_INJECTION_PATTERN.search(body_str):
@@ -142,7 +140,9 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
142
  extra={
143
  "path": str(request.url.path),
144
  "method": request.method,
145
- "client_ip": (request.client.host if request.client else None),
 
 
146
  },
147
  )
148
  raise HTTPException(
@@ -156,13 +156,6 @@ class InputValidationMiddleware(BaseHTTPMiddleware):
156
  # Check query parameters for attacks
157
  query_string = str(request.url.query)
158
  if query_string:
159
- if self.SQL_INJECTION_PATTERN.search(query_string):
160
- logger.error(
161
- "SQL injection in query parameters",
162
- extra={"path": str(request.url.path), "query": query_string},
163
- )
164
- raise HTTPException(status_code=400, detail="Invalid query parameters")
165
-
166
  if self.PATH_TRAVERSAL_PATTERN.search(query_string):
167
  logger.error(
168
  "Path traversal in query parameters",
@@ -230,7 +223,9 @@ class CaseStatus(str, Enum):
230
  # Common validation patterns
231
  SAFE_STRING_PATTERN = re.compile(r"^[a-zA-Z0-9\s\-_\.,!?()]+$")
232
  EMAIL_PATTERN = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
233
- UUID_PATTERN = re.compile(r"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
 
 
234
 
235
 
236
  def validate_safe_string(v: str) -> str:
@@ -277,7 +272,9 @@ class UserCreateRequest(BaseModel):
277
  @classmethod
278
  def username_alphanumeric(cls, v):
279
  if not re.match(r"^[a-zA-Z0-9_-]+$", v):
280
- raise ValueError("Username must be alphanumeric with underscores and hyphens only")
 
 
281
  return v
282
 
283
 
@@ -288,22 +285,32 @@ class UserLoginRequest(BaseModel):
288
 
289
  class CaseCreateRequest(BaseModel):
290
  title: constr(min_length=1, max_length=200) = Field(description="Case title")
291
- description: constr(max_length=2000) | None = Field(None, description="Case description")
 
 
292
  assigned_to: UUIDStr | None = Field(None, description="Assigned user ID")
293
 
294
 
295
  class CaseUpdateRequest(BaseModel):
296
- title: constr(min_length=1, max_length=200) | None = Field(None, description="Case title")
297
- description: constr(max_length=2000) | None = Field(None, description="Case description")
 
 
 
 
298
  status: CaseStatus | None = Field(None, description="Case status")
299
  assigned_to: UUIDStr | None = Field(None, description="Assigned user ID")
300
 
301
 
302
  class EvidenceUploadRequest(BaseModel):
303
  case_id: UUIDStr = Field(description="Case ID")
304
- filename: constr(min_length=1, max_length=255) = Field(description="Original filename")
 
 
305
  file_type: SafeString = Field(description="MIME type")
306
- size_bytes: int = Field(ge=0, le=100 * 1024 * 1024, description="File size in bytes") # Max 100MB
 
 
307
 
308
  @field_validator("file_type")
309
  @classmethod
 
25
  # Maximum request size (10MB)
26
  MAX_REQUEST_SIZE = 10 * 1024 * 1024
27
 
28
+ # XSS pattern detection - targets actual XSS attack vectors
29
+ XSS_PATTERN = re.compile(
30
+ r"(<script|javascript:|data:text/html|<iframe|<object|<embed|on\w+=)",
31
  re.IGNORECASE,
32
  )
33
 
 
 
34
  # Path traversal detection
35
+ PATH_TRAVERSAL_PATTERN = re.compile(
36
+ r"(\.\./|\.\.\\|%2e%2e%2f|%2e%2e/|\.\.%2f|%2e%2e%5c)", re.IGNORECASE
37
+ )
38
 
39
  # Command injection detection
40
  COMMAND_INJECTION_PATTERN = re.compile(
 
79
  if "multipart/form-data" in content_type:
80
  # Allow multipart for file uploads, will validate individual parts
81
  pass
82
+ elif (
83
+ content_type not in self.ALLOWED_CONTENT_TYPES
84
+ and content_type != "application/x-www-form-urlencoded"
85
+ ):
86
  logger.warning(
87
  "Invalid content type",
88
  extra={
 
91
  "client_ip": request.client.host if request.client else None,
92
  },
93
  )
94
+ raise HTTPException(
95
+ status_code=415, detail=f"Unsupported content type: {content_type}"
96
+ )
97
 
98
  # Get request body for POST/PUT/PATCH
99
  if request.method in ["POST", "PUT", "PATCH"]:
 
101
  body = await request.body()
102
  body_str = body.decode("utf-8")
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  # Check for XSS patterns
105
  if self.XSS_PATTERN.search(body_str):
106
  logger.error(
 
108
  extra={
109
  "path": str(request.url.path),
110
  "method": request.method,
111
+ "client_ip": (
112
+ request.client.host if request.client else None
113
+ ),
114
  },
115
  )
116
+ raise HTTPException(
117
+ status_code=400, detail="Invalid input: Potential XSS detected"
118
+ )
119
 
120
  # Check for path traversal
121
  if self.PATH_TRAVERSAL_PATTERN.search(body_str):
 
124
  extra={
125
  "path": str(request.url.path),
126
  "method": request.method,
127
+ "client_ip": (
128
+ request.client.host if request.client else None
129
+ ),
130
  },
131
  )
132
+ raise HTTPException(
133
+ status_code=400, detail="Invalid input: Path traversal detected"
134
+ )
135
 
136
  # Check for command injection
137
  if self.COMMAND_INJECTION_PATTERN.search(body_str):
 
140
  extra={
141
  "path": str(request.url.path),
142
  "method": request.method,
143
+ "client_ip": (
144
+ request.client.host if request.client else None
145
+ ),
146
  },
147
  )
148
  raise HTTPException(
 
156
  # Check query parameters for attacks
157
  query_string = str(request.url.query)
158
  if query_string:
 
 
 
 
 
 
 
159
  if self.PATH_TRAVERSAL_PATTERN.search(query_string):
160
  logger.error(
161
  "Path traversal in query parameters",
 
223
  # Common validation patterns
224
  SAFE_STRING_PATTERN = re.compile(r"^[a-zA-Z0-9\s\-_\.,!?()]+$")
225
  EMAIL_PATTERN = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
226
+ UUID_PATTERN = re.compile(
227
+ r"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$"
228
+ )
229
 
230
 
231
  def validate_safe_string(v: str) -> str:
 
272
  @classmethod
273
  def username_alphanumeric(cls, v):
274
  if not re.match(r"^[a-zA-Z0-9_-]+$", v):
275
+ raise ValueError(
276
+ "Username must be alphanumeric with underscores and hyphens only"
277
+ )
278
  return v
279
 
280
 
 
285
 
286
  class CaseCreateRequest(BaseModel):
287
  title: constr(min_length=1, max_length=200) = Field(description="Case title")
288
+ description: constr(max_length=2000) | None = Field(
289
+ None, description="Case description"
290
+ )
291
  assigned_to: UUIDStr | None = Field(None, description="Assigned user ID")
292
 
293
 
294
  class CaseUpdateRequest(BaseModel):
295
+ title: constr(min_length=1, max_length=200) | None = Field(
296
+ None, description="Case title"
297
+ )
298
+ description: constr(max_length=2000) | None = Field(
299
+ None, description="Case description"
300
+ )
301
  status: CaseStatus | None = Field(None, description="Case status")
302
  assigned_to: UUIDStr | None = Field(None, description="Assigned user ID")
303
 
304
 
305
  class EvidenceUploadRequest(BaseModel):
306
  case_id: UUIDStr = Field(description="Case ID")
307
+ filename: constr(min_length=1, max_length=255) = Field(
308
+ description="Original filename"
309
+ )
310
  file_type: SafeString = Field(description="MIME type")
311
+ size_bytes: int = Field(
312
+ ge=0, le=100 * 1024 * 1024, description="File size in bytes"
313
+ ) # Max 100MB
314
 
315
  @field_validator("file_type")
316
  @classmethod