-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidators.py
More file actions
145 lines (112 loc) · 4.47 KB
/
validators.py
File metadata and controls
145 lines (112 loc) · 4.47 KB
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
"""
CodeSense - Input Validation
Validates all user inputs before processing.
"""
import re
import os
from pathlib import Path
from typing import Tuple
from constants import (
SUPPORTED_LANGUAGES, LANGUAGE_EXTENSIONS,
MAX_FILE_SIZE_MB, MAX_CODE_LINES, MIN_CODE_LINES,
)
from logger import get_logger
logger = get_logger(__name__)
class ValidationError(Exception):
"""Raised when input validation fails."""
def __init__(self, message: str, field: str = "general"):
super().__init__(message)
self.field = field
self.message = message
def validate_code(code: str) -> Tuple[bool, str]:
"""
Validate raw code input.
Returns:
(is_valid, error_message)
"""
if not code or not code.strip():
return False, "Code cannot be empty."
lines = code.splitlines()
if len(lines) < MIN_CODE_LINES:
return False, f"Code must have at least {MIN_CODE_LINES} lines."
if len(lines) > MAX_CODE_LINES:
return False, f"Code exceeds maximum of {MAX_CODE_LINES} lines."
size_mb = len(code.encode("utf-8")) / (1024 * 1024)
if size_mb > MAX_FILE_SIZE_MB:
return False, f"Code size ({size_mb:.1f} MB) exceeds limit of {MAX_FILE_SIZE_MB} MB."
return True, ""
def validate_language(language: str) -> Tuple[bool, str]:
"""Ensure language is supported."""
if language.lower() not in SUPPORTED_LANGUAGES:
return False, f"Language '{language}' is not supported. Choose from: {', '.join(SUPPORTED_LANGUAGES)}"
return True, ""
def validate_file(file_path: str) -> Tuple[bool, str, str]:
"""
Validate uploaded file path.
Returns:
(is_valid, error_message, detected_language)
"""
path = Path(file_path)
if not path.exists():
return False, "File does not exist.", ""
if not path.is_file():
return False, "Path is not a file.", ""
suffix = path.suffix.lower()
if suffix not in LANGUAGE_EXTENSIONS:
supported = ", ".join(LANGUAGE_EXTENSIONS.keys())
return False, f"Unsupported file type '{suffix}'. Supported: {supported}", ""
size_mb = path.stat().st_size / (1024 * 1024)
if size_mb > MAX_FILE_SIZE_MB:
return False, f"File size ({size_mb:.1f} MB) exceeds limit of {MAX_FILE_SIZE_MB} MB.", ""
language = LANGUAGE_EXTENSIONS[suffix]
return True, "", language
def validate_email(email: str) -> Tuple[bool, str]:
"""Validate email address format."""
pattern = r"^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$"
if not re.match(pattern, email.strip()):
return False, "Invalid email address format."
return True, ""
def validate_password(password: str) -> Tuple[bool, str]:
"""
Enforce password policy:
- At least 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one digit
- At least one special character
"""
if len(password) < 8:
return False, "Password must be at least 8 characters."
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."
if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", password):
return False, "Password must contain at least one special character."
return True, ""
def validate_username(username: str) -> Tuple[bool, str]:
"""Validate username."""
if len(username) < 3:
return False, "Username must be at least 3 characters."
if len(username) > 30:
return False, "Username must be 30 characters or fewer."
if not re.match(r"^[a-zA-Z0-9_.-]+$", username):
return False, "Username may only contain letters, digits, underscores, hyphens, and dots."
return True, ""
def validate_github_url(url: str) -> Tuple[bool, str]:
"""Validate GitHub raw or repo URL."""
patterns = [
r"^https://github\.com/[\w.\-]+/[\w.\-]+",
r"^https://raw\.githubusercontent\.com/",
]
for pattern in patterns:
if re.match(pattern, url):
return True, ""
return False, "Invalid GitHub URL. Must be a github.com or raw.githubusercontent.com URL."
def sanitize_code(code: str) -> str:
"""Strip null bytes and normalize line endings."""
code = code.replace("\x00", "")
code = code.replace("\r\n", "\n").replace("\r", "\n")
return code