createsonline 0.1.26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- createsonline/__init__.py +46 -0
- createsonline/admin/__init__.py +7 -0
- createsonline/admin/content.py +526 -0
- createsonline/admin/crud.py +805 -0
- createsonline/admin/field_builder.py +559 -0
- createsonline/admin/integration.py +482 -0
- createsonline/admin/interface.py +2562 -0
- createsonline/admin/model_creator.py +513 -0
- createsonline/admin/model_manager.py +388 -0
- createsonline/admin/modern_dashboard.py +498 -0
- createsonline/admin/permissions.py +264 -0
- createsonline/admin/user_forms.py +594 -0
- createsonline/ai/__init__.py +202 -0
- createsonline/ai/fields.py +1226 -0
- createsonline/ai/orm.py +325 -0
- createsonline/ai/services.py +1244 -0
- createsonline/app.py +506 -0
- createsonline/auth/__init__.py +8 -0
- createsonline/auth/management.py +228 -0
- createsonline/auth/models.py +552 -0
- createsonline/cli/__init__.py +5 -0
- createsonline/cli/commands/__init__.py +122 -0
- createsonline/cli/commands/database.py +416 -0
- createsonline/cli/commands/info.py +173 -0
- createsonline/cli/commands/initdb.py +218 -0
- createsonline/cli/commands/project.py +545 -0
- createsonline/cli/commands/serve.py +173 -0
- createsonline/cli/commands/shell.py +93 -0
- createsonline/cli/commands/users.py +148 -0
- createsonline/cli/main.py +2041 -0
- createsonline/cli/manage.py +274 -0
- createsonline/config/__init__.py +9 -0
- createsonline/config/app.py +2577 -0
- createsonline/config/database.py +179 -0
- createsonline/config/docs.py +384 -0
- createsonline/config/errors.py +160 -0
- createsonline/config/orm.py +43 -0
- createsonline/config/request.py +93 -0
- createsonline/config/settings.py +176 -0
- createsonline/data/__init__.py +23 -0
- createsonline/data/dataframe.py +925 -0
- createsonline/data/io.py +453 -0
- createsonline/data/series.py +557 -0
- createsonline/database/__init__.py +60 -0
- createsonline/database/abstraction.py +440 -0
- createsonline/database/assistant.py +585 -0
- createsonline/database/fields.py +442 -0
- createsonline/database/migrations.py +132 -0
- createsonline/database/models.py +604 -0
- createsonline/database.py +438 -0
- createsonline/http/__init__.py +28 -0
- createsonline/http/client.py +535 -0
- createsonline/ml/__init__.py +55 -0
- createsonline/ml/classification.py +552 -0
- createsonline/ml/clustering.py +680 -0
- createsonline/ml/metrics.py +542 -0
- createsonline/ml/neural.py +560 -0
- createsonline/ml/preprocessing.py +784 -0
- createsonline/ml/regression.py +501 -0
- createsonline/performance/__init__.py +19 -0
- createsonline/performance/cache.py +444 -0
- createsonline/performance/compression.py +335 -0
- createsonline/performance/core.py +419 -0
- createsonline/project_init.py +789 -0
- createsonline/routing.py +528 -0
- createsonline/security/__init__.py +34 -0
- createsonline/security/core.py +811 -0
- createsonline/security/encryption.py +349 -0
- createsonline/server.py +295 -0
- createsonline/static/css/admin.css +263 -0
- createsonline/static/css/common.css +358 -0
- createsonline/static/css/dashboard.css +89 -0
- createsonline/static/favicon.ico +0 -0
- createsonline/static/icons/icon-128x128.png +0 -0
- createsonline/static/icons/icon-128x128.webp +0 -0
- createsonline/static/icons/icon-16x16.png +0 -0
- createsonline/static/icons/icon-16x16.webp +0 -0
- createsonline/static/icons/icon-180x180.png +0 -0
- createsonline/static/icons/icon-180x180.webp +0 -0
- createsonline/static/icons/icon-192x192.png +0 -0
- createsonline/static/icons/icon-192x192.webp +0 -0
- createsonline/static/icons/icon-256x256.png +0 -0
- createsonline/static/icons/icon-256x256.webp +0 -0
- createsonline/static/icons/icon-32x32.png +0 -0
- createsonline/static/icons/icon-32x32.webp +0 -0
- createsonline/static/icons/icon-384x384.png +0 -0
- createsonline/static/icons/icon-384x384.webp +0 -0
- createsonline/static/icons/icon-48x48.png +0 -0
- createsonline/static/icons/icon-48x48.webp +0 -0
- createsonline/static/icons/icon-512x512.png +0 -0
- createsonline/static/icons/icon-512x512.webp +0 -0
- createsonline/static/icons/icon-64x64.png +0 -0
- createsonline/static/icons/icon-64x64.webp +0 -0
- createsonline/static/image/android-chrome-192x192.png +0 -0
- createsonline/static/image/android-chrome-512x512.png +0 -0
- createsonline/static/image/apple-touch-icon.png +0 -0
- createsonline/static/image/favicon-16x16.png +0 -0
- createsonline/static/image/favicon-32x32.png +0 -0
- createsonline/static/image/favicon.ico +0 -0
- createsonline/static/image/favicon.svg +17 -0
- createsonline/static/image/icon-128x128.png +0 -0
- createsonline/static/image/icon-128x128.webp +0 -0
- createsonline/static/image/icon-16x16.png +0 -0
- createsonline/static/image/icon-16x16.webp +0 -0
- createsonline/static/image/icon-180x180.png +0 -0
- createsonline/static/image/icon-180x180.webp +0 -0
- createsonline/static/image/icon-192x192.png +0 -0
- createsonline/static/image/icon-192x192.webp +0 -0
- createsonline/static/image/icon-256x256.png +0 -0
- createsonline/static/image/icon-256x256.webp +0 -0
- createsonline/static/image/icon-32x32.png +0 -0
- createsonline/static/image/icon-32x32.webp +0 -0
- createsonline/static/image/icon-384x384.png +0 -0
- createsonline/static/image/icon-384x384.webp +0 -0
- createsonline/static/image/icon-48x48.png +0 -0
- createsonline/static/image/icon-48x48.webp +0 -0
- createsonline/static/image/icon-512x512.png +0 -0
- createsonline/static/image/icon-512x512.webp +0 -0
- createsonline/static/image/icon-64x64.png +0 -0
- createsonline/static/image/icon-64x64.webp +0 -0
- createsonline/static/image/logo-header-h100.png +0 -0
- createsonline/static/image/logo-header-h100.webp +0 -0
- createsonline/static/image/logo-header-h200@2x.png +0 -0
- createsonline/static/image/logo-header-h200@2x.webp +0 -0
- createsonline/static/image/logo.png +0 -0
- createsonline/static/js/admin.js +274 -0
- createsonline/static/site.webmanifest +35 -0
- createsonline/static/templates/admin/base.html +87 -0
- createsonline/static/templates/admin/dashboard.html +217 -0
- createsonline/static/templates/admin/model_form.html +270 -0
- createsonline/static/templates/admin/model_list.html +202 -0
- createsonline/static/test_script.js +15 -0
- createsonline/static/test_styles.css +59 -0
- createsonline/static_files.py +365 -0
- createsonline/templates/404.html +100 -0
- createsonline/templates/admin_login.html +169 -0
- createsonline/templates/base.html +102 -0
- createsonline/templates/index.html +151 -0
- createsonline/templates.py +205 -0
- createsonline/testing.py +322 -0
- createsonline/utils.py +448 -0
- createsonline/validation/__init__.py +49 -0
- createsonline/validation/fields.py +598 -0
- createsonline/validation/models.py +504 -0
- createsonline/validation/validators.py +561 -0
- createsonline/views.py +184 -0
- createsonline-0.1.26.dist-info/METADATA +46 -0
- createsonline-0.1.26.dist-info/RECORD +152 -0
- createsonline-0.1.26.dist-info/WHEEL +5 -0
- createsonline-0.1.26.dist-info/entry_points.txt +2 -0
- createsonline-0.1.26.dist-info/licenses/LICENSE +21 -0
- createsonline-0.1.26.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
# createsonline/security/core.py
|
|
2
|
+
"""
|
|
3
|
+
CREATESONLINE Security Core
|
|
4
|
+
|
|
5
|
+
Ultra-high security manager that prevents all common vulnerabilities:
|
|
6
|
+
- OWASP Top 10 protection
|
|
7
|
+
- Zero-trust architecture
|
|
8
|
+
- Multi-layer security
|
|
9
|
+
- Real-time threat detection
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import hashlib
|
|
13
|
+
import hmac
|
|
14
|
+
import time
|
|
15
|
+
import secrets
|
|
16
|
+
import re
|
|
17
|
+
import json
|
|
18
|
+
from typing import Dict, Any, Optional, List, Callable, Union
|
|
19
|
+
from collections import defaultdict
|
|
20
|
+
import threading
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SecurityManager:
|
|
24
|
+
"""
|
|
25
|
+
Ultra-high security manager for CREATESONLINE framework
|
|
26
|
+
Implements enterprise-grade security measures
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, secret_key: Optional[str] = None):
|
|
30
|
+
self.secret_key = secret_key or self._generate_secret_key()
|
|
31
|
+
|
|
32
|
+
# Security policies
|
|
33
|
+
self.password_policy = PasswordPolicy()
|
|
34
|
+
self.rate_limiter = RateLimiter()
|
|
35
|
+
self.csrf_protection = CSRFProtection(self.secret_key)
|
|
36
|
+
self.xss_protection = XSSProtection()
|
|
37
|
+
self.sql_protection = SQLInjectionProtection()
|
|
38
|
+
self.input_validator = InputValidator()
|
|
39
|
+
|
|
40
|
+
# Security monitoring
|
|
41
|
+
self.threat_detector = ThreatDetector()
|
|
42
|
+
self.audit_logger = AuditLogger()
|
|
43
|
+
self.security_metrics = SecurityMetrics()
|
|
44
|
+
|
|
45
|
+
# Security headers
|
|
46
|
+
self.security_headers = self._get_security_headers()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _generate_secret_key(self) -> str:
|
|
50
|
+
"""Generate cryptographically secure secret key"""
|
|
51
|
+
return secrets.token_urlsafe(64)
|
|
52
|
+
|
|
53
|
+
def _get_security_headers(self) -> Dict[str, str]:
|
|
54
|
+
"""Get security headers to prevent various attacks"""
|
|
55
|
+
return {
|
|
56
|
+
# XSS Protection
|
|
57
|
+
'X-XSS-Protection': '1; mode=block',
|
|
58
|
+
'X-Content-Type-Options': 'nosniff',
|
|
59
|
+
|
|
60
|
+
# Clickjacking Protection
|
|
61
|
+
'X-Frame-Options': 'DENY',
|
|
62
|
+
|
|
63
|
+
# HTTPS Enforcement
|
|
64
|
+
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
|
|
65
|
+
|
|
66
|
+
# Content Security Policy
|
|
67
|
+
'Content-Security-Policy': (
|
|
68
|
+
"default-src 'self'; "
|
|
69
|
+
"script-src 'self' 'unsafe-inline'; "
|
|
70
|
+
"style-src 'self' 'unsafe-inline'; "
|
|
71
|
+
"img-src 'self' data: https:; "
|
|
72
|
+
"font-src 'self'; "
|
|
73
|
+
"connect-src 'self'; "
|
|
74
|
+
"frame-ancestors 'none'"
|
|
75
|
+
),
|
|
76
|
+
|
|
77
|
+
# Referrer Policy
|
|
78
|
+
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
|
79
|
+
|
|
80
|
+
# Feature Policy
|
|
81
|
+
'Permissions-Policy': (
|
|
82
|
+
'geolocation=(), microphone=(), camera=(), '
|
|
83
|
+
'payment=(), usb=(), magnetometer=(), gyroscope=()'
|
|
84
|
+
),
|
|
85
|
+
|
|
86
|
+
# Framework Identification
|
|
87
|
+
'X-Framework': 'CREATESONLINE-Secured',
|
|
88
|
+
'X-Security-Level': 'Enterprise'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async def process_request(self, request, response_callback: Callable) -> Any:
|
|
92
|
+
"""Process request through all security layers"""
|
|
93
|
+
|
|
94
|
+
# 1. Rate limiting check
|
|
95
|
+
if not await self.rate_limiter.check_request(request):
|
|
96
|
+
return self._create_error_response(429, "Rate limit exceeded")
|
|
97
|
+
|
|
98
|
+
# 2. Input validation
|
|
99
|
+
if not await self.input_validator.validate_request(request):
|
|
100
|
+
return self._create_error_response(400, "Invalid input detected")
|
|
101
|
+
|
|
102
|
+
# 3. SQL injection protection
|
|
103
|
+
if not await self.sql_protection.check_request(request):
|
|
104
|
+
return self._create_error_response(400, "Potential SQL injection detected")
|
|
105
|
+
|
|
106
|
+
# 4. XSS protection
|
|
107
|
+
if not await self.xss_protection.check_request(request):
|
|
108
|
+
return self._create_error_response(400, "Potential XSS attack detected")
|
|
109
|
+
|
|
110
|
+
# 5. CSRF protection (for state-changing operations)
|
|
111
|
+
if request.method in ['POST', 'PUT', 'DELETE', 'PATCH']:
|
|
112
|
+
if not await self.csrf_protection.verify_token(request):
|
|
113
|
+
return self._create_error_response(403, "CSRF token invalid")
|
|
114
|
+
|
|
115
|
+
# 6. Threat detection
|
|
116
|
+
threat_level = await self.threat_detector.analyze_request(request)
|
|
117
|
+
if threat_level > 0.8: # High threat threshold
|
|
118
|
+
self.audit_logger.log_security_event(request, "High threat detected", threat_level)
|
|
119
|
+
return self._create_error_response(403, "Request blocked by security system")
|
|
120
|
+
|
|
121
|
+
# 7. Execute request
|
|
122
|
+
try:
|
|
123
|
+
response = await response_callback()
|
|
124
|
+
|
|
125
|
+
# 8. Secure response headers
|
|
126
|
+
secured_response = await self._secure_response(response)
|
|
127
|
+
|
|
128
|
+
# 9. Log successful request
|
|
129
|
+
self.audit_logger.log_request(request, "success")
|
|
130
|
+
self.security_metrics.record_request()
|
|
131
|
+
|
|
132
|
+
return secured_response
|
|
133
|
+
|
|
134
|
+
except Exception as e:
|
|
135
|
+
# Log security exception
|
|
136
|
+
self.audit_logger.log_security_event(request, f"Request failed: {str(e)}")
|
|
137
|
+
self.security_metrics.record_error()
|
|
138
|
+
raise
|
|
139
|
+
|
|
140
|
+
async def _secure_response(self, response) -> Any:
|
|
141
|
+
"""Add security headers to response"""
|
|
142
|
+
|
|
143
|
+
if hasattr(response, 'headers'):
|
|
144
|
+
# Add security headers
|
|
145
|
+
for header, value in self.security_headers.items():
|
|
146
|
+
response.headers[header] = value
|
|
147
|
+
|
|
148
|
+
# Content filtering for XSS prevention
|
|
149
|
+
if hasattr(response, 'body') and isinstance(response.body, (str, bytes)):
|
|
150
|
+
response.body = self.xss_protection.sanitize_output(response.body)
|
|
151
|
+
|
|
152
|
+
return response
|
|
153
|
+
|
|
154
|
+
def _create_error_response(self, status_code: int, message: str) -> Dict[str, Any]:
|
|
155
|
+
"""Create secure error response"""
|
|
156
|
+
return {
|
|
157
|
+
'status_code': status_code,
|
|
158
|
+
'body': json.dumps({
|
|
159
|
+
'error': message,
|
|
160
|
+
'framework': 'CREATESONLINE',
|
|
161
|
+
'security': 'protected',
|
|
162
|
+
'timestamp': time.time()
|
|
163
|
+
}),
|
|
164
|
+
'headers': {
|
|
165
|
+
**self.security_headers,
|
|
166
|
+
'Content-Type': 'application/json'
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
def get_security_status(self) -> Dict[str, Any]:
|
|
171
|
+
"""Get comprehensive security status"""
|
|
172
|
+
return {
|
|
173
|
+
'security_level': 'Enterprise',
|
|
174
|
+
'protections_active': [
|
|
175
|
+
'Rate Limiting',
|
|
176
|
+
'Input Validation',
|
|
177
|
+
'SQL Injection Protection',
|
|
178
|
+
'XSS Protection',
|
|
179
|
+
'CSRF Protection',
|
|
180
|
+
'Threat Detection',
|
|
181
|
+
'Security Headers',
|
|
182
|
+
'Password Policy',
|
|
183
|
+
'Audit Logging'
|
|
184
|
+
],
|
|
185
|
+
'owasp_compliance': 'Full Top 10 Protection',
|
|
186
|
+
'metrics': self.security_metrics.get_stats(),
|
|
187
|
+
'threat_level': 'Low',
|
|
188
|
+
'last_scan': time.time()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class PasswordPolicy:
|
|
193
|
+
"""Enterprise password policy enforcement"""
|
|
194
|
+
|
|
195
|
+
def __init__(self):
|
|
196
|
+
self.min_length = 12
|
|
197
|
+
self.require_uppercase = True
|
|
198
|
+
self.require_lowercase = True
|
|
199
|
+
self.require_numbers = True
|
|
200
|
+
self.require_symbols = True
|
|
201
|
+
self.forbidden_patterns = [
|
|
202
|
+
'password', '123456', 'qwerty', 'admin', 'user',
|
|
203
|
+
'login', 'welcome', 'default', 'system'
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
def validate_password(self, password: str) -> Dict[str, Any]:
|
|
207
|
+
"""Validate password against policy"""
|
|
208
|
+
|
|
209
|
+
errors = []
|
|
210
|
+
score = 0
|
|
211
|
+
|
|
212
|
+
# Length check
|
|
213
|
+
if len(password) < self.min_length:
|
|
214
|
+
errors.append(f"Password must be at least {self.min_length} characters")
|
|
215
|
+
else:
|
|
216
|
+
score += 20
|
|
217
|
+
|
|
218
|
+
# Character type checks
|
|
219
|
+
if self.require_uppercase and not re.search(r'[A-Z]', password):
|
|
220
|
+
errors.append("Password must contain uppercase letters")
|
|
221
|
+
else:
|
|
222
|
+
score += 15
|
|
223
|
+
|
|
224
|
+
if self.require_lowercase and not re.search(r'[a-z]', password):
|
|
225
|
+
errors.append("Password must contain lowercase letters")
|
|
226
|
+
else:
|
|
227
|
+
score += 15
|
|
228
|
+
|
|
229
|
+
if self.require_numbers and not re.search(r'\d', password):
|
|
230
|
+
errors.append("Password must contain numbers")
|
|
231
|
+
else:
|
|
232
|
+
score += 20
|
|
233
|
+
|
|
234
|
+
if self.require_symbols and not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
|
|
235
|
+
errors.append("Password must contain special characters")
|
|
236
|
+
else:
|
|
237
|
+
score += 20
|
|
238
|
+
|
|
239
|
+
# Forbidden patterns
|
|
240
|
+
password_lower = password.lower()
|
|
241
|
+
for pattern in self.forbidden_patterns:
|
|
242
|
+
if pattern in password_lower:
|
|
243
|
+
errors.append(f"Password cannot contain '{pattern}'")
|
|
244
|
+
score -= 30
|
|
245
|
+
|
|
246
|
+
# Complexity bonus
|
|
247
|
+
if len(password) > 16:
|
|
248
|
+
score += 10
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
'valid': len(errors) == 0,
|
|
252
|
+
'errors': errors,
|
|
253
|
+
'strength_score': max(0, min(100, score)),
|
|
254
|
+
'strength_level': self._get_strength_level(score)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
def _get_strength_level(self, score: int) -> str:
|
|
258
|
+
"""Get password strength level"""
|
|
259
|
+
if score >= 90:
|
|
260
|
+
return 'Very Strong'
|
|
261
|
+
elif score >= 70:
|
|
262
|
+
return 'Strong'
|
|
263
|
+
elif score >= 50:
|
|
264
|
+
return 'Medium'
|
|
265
|
+
elif score >= 30:
|
|
266
|
+
return 'Weak'
|
|
267
|
+
else:
|
|
268
|
+
return 'Very Weak'
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class ThreatDetector:
|
|
272
|
+
"""Real-time threat detection system"""
|
|
273
|
+
|
|
274
|
+
def __init__(self):
|
|
275
|
+
self.suspicious_patterns = [
|
|
276
|
+
# SQL Injection patterns
|
|
277
|
+
r'(union\s+select|drop\s+table|insert\s+into|delete\s+from)',
|
|
278
|
+
r'(or\s+1\s*=\s*1|and\s+1\s*=\s*1)',
|
|
279
|
+
r'(exec\s*\(|execute\s*\(|sp_)',
|
|
280
|
+
|
|
281
|
+
# XSS patterns
|
|
282
|
+
r'(<script|javascript:|vbscript:)',
|
|
283
|
+
r'(onload\s*=|onclick\s*=|onmouseover\s*=)',
|
|
284
|
+
r'(document\.cookie|window\.location)',
|
|
285
|
+
|
|
286
|
+
# Directory traversal
|
|
287
|
+
r'(\.\./|\.\.\\)',
|
|
288
|
+
r'(/etc/passwd|/windows/system32)',
|
|
289
|
+
|
|
290
|
+
# Command injection
|
|
291
|
+
r'(;|\||&)\s*(cat|ls|dir|type|echo)',
|
|
292
|
+
r'(\$\(|\`)',
|
|
293
|
+
|
|
294
|
+
# File inclusion
|
|
295
|
+
r'(include\s*\(|require\s*\()',
|
|
296
|
+
r'(php://|file://|data://)'
|
|
297
|
+
]
|
|
298
|
+
|
|
299
|
+
self.ip_reputation = {}
|
|
300
|
+
self.request_patterns = defaultdict(list)
|
|
301
|
+
|
|
302
|
+
async def analyze_request(self, request) -> float:
|
|
303
|
+
"""Analyze request for threats (0.0 = safe, 1.0 = dangerous)"""
|
|
304
|
+
|
|
305
|
+
threat_score = 0.0
|
|
306
|
+
|
|
307
|
+
# Get client IP
|
|
308
|
+
client_ip = self._get_client_ip(request)
|
|
309
|
+
|
|
310
|
+
# Check IP reputation
|
|
311
|
+
ip_reputation = self.ip_reputation.get(client_ip, 0.0)
|
|
312
|
+
threat_score += ip_reputation * 0.3
|
|
313
|
+
|
|
314
|
+
# Analyze request content
|
|
315
|
+
content_score = await self._analyze_content(request)
|
|
316
|
+
threat_score += content_score * 0.4
|
|
317
|
+
|
|
318
|
+
# Pattern analysis
|
|
319
|
+
pattern_score = self._analyze_patterns(request, client_ip)
|
|
320
|
+
threat_score += pattern_score * 0.3
|
|
321
|
+
|
|
322
|
+
# Update IP reputation
|
|
323
|
+
if threat_score > 0.5:
|
|
324
|
+
self.ip_reputation[client_ip] = min(1.0, self.ip_reputation.get(client_ip, 0) + 0.1)
|
|
325
|
+
|
|
326
|
+
return min(1.0, threat_score)
|
|
327
|
+
|
|
328
|
+
async def _analyze_content(self, request) -> float:
|
|
329
|
+
"""Analyze request content for malicious patterns"""
|
|
330
|
+
|
|
331
|
+
score = 0.0
|
|
332
|
+
|
|
333
|
+
# Check URL path
|
|
334
|
+
path = getattr(request, 'path', '')
|
|
335
|
+
for pattern in self.suspicious_patterns:
|
|
336
|
+
if re.search(pattern, path, re.IGNORECASE):
|
|
337
|
+
score += 0.3
|
|
338
|
+
|
|
339
|
+
# Check query parameters
|
|
340
|
+
query_params = getattr(request, 'query_params', {})
|
|
341
|
+
for key, value in query_params.items():
|
|
342
|
+
content = f"{key}={value}"
|
|
343
|
+
for pattern in self.suspicious_patterns:
|
|
344
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
345
|
+
score += 0.2
|
|
346
|
+
|
|
347
|
+
# Check headers
|
|
348
|
+
headers = getattr(request, 'headers', {})
|
|
349
|
+
for header, value in headers.items():
|
|
350
|
+
if isinstance(value, str):
|
|
351
|
+
for pattern in self.suspicious_patterns:
|
|
352
|
+
if re.search(pattern, value, re.IGNORECASE):
|
|
353
|
+
score += 0.1
|
|
354
|
+
|
|
355
|
+
return min(1.0, score)
|
|
356
|
+
|
|
357
|
+
def _analyze_patterns(self, request, client_ip: str) -> float:
|
|
358
|
+
"""Analyze request patterns for suspicious behavior"""
|
|
359
|
+
|
|
360
|
+
current_time = time.time()
|
|
361
|
+
|
|
362
|
+
# Record request
|
|
363
|
+
self.request_patterns[client_ip].append({
|
|
364
|
+
'timestamp': current_time,
|
|
365
|
+
'path': getattr(request, 'path', ''),
|
|
366
|
+
'method': getattr(request, 'method', 'GET')
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
# Clean old requests (keep last hour)
|
|
370
|
+
self.request_patterns[client_ip] = [
|
|
371
|
+
req for req in self.request_patterns[client_ip]
|
|
372
|
+
if current_time - req['timestamp'] < 3600
|
|
373
|
+
]
|
|
374
|
+
|
|
375
|
+
requests = self.request_patterns[client_ip]
|
|
376
|
+
|
|
377
|
+
if len(requests) < 2:
|
|
378
|
+
return 0.0
|
|
379
|
+
|
|
380
|
+
score = 0.0
|
|
381
|
+
|
|
382
|
+
# Check for rapid requests (potential DoS)
|
|
383
|
+
recent_requests = [r for r in requests if current_time - r['timestamp'] < 60]
|
|
384
|
+
if len(recent_requests) > 100:
|
|
385
|
+
score += 0.6
|
|
386
|
+
elif len(recent_requests) > 50:
|
|
387
|
+
score += 0.3
|
|
388
|
+
|
|
389
|
+
# Check for path scanning
|
|
390
|
+
unique_paths = set(req['path'] for req in requests[-20:])
|
|
391
|
+
if len(unique_paths) > 15: # Too many different paths
|
|
392
|
+
score += 0.4
|
|
393
|
+
|
|
394
|
+
# Check for error-generating patterns
|
|
395
|
+
error_patterns = ['/admin', '/.env', '/config', '/api/v1', '/wp-admin']
|
|
396
|
+
error_requests = sum(1 for req in requests[-10:]
|
|
397
|
+
if any(pattern in req['path'] for pattern in error_patterns))
|
|
398
|
+
if error_requests > 5:
|
|
399
|
+
score += 0.3
|
|
400
|
+
|
|
401
|
+
return min(1.0, score)
|
|
402
|
+
|
|
403
|
+
def _get_client_ip(self, request) -> str:
|
|
404
|
+
"""Extract client IP from request"""
|
|
405
|
+
|
|
406
|
+
# Check X-Forwarded-For header
|
|
407
|
+
headers = getattr(request, 'headers', {})
|
|
408
|
+
forwarded = headers.get('x-forwarded-for', '')
|
|
409
|
+
if forwarded:
|
|
410
|
+
return forwarded.split(',')[0].strip()
|
|
411
|
+
|
|
412
|
+
# Check X-Real-IP header
|
|
413
|
+
real_ip = headers.get('x-real-ip', '')
|
|
414
|
+
if real_ip:
|
|
415
|
+
return real_ip
|
|
416
|
+
|
|
417
|
+
# Fallback to client host
|
|
418
|
+
client = getattr(request, 'client', {})
|
|
419
|
+
return client.get('host', '127.0.0.1')
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class AuditLogger:
|
|
423
|
+
"""Security audit logging system"""
|
|
424
|
+
|
|
425
|
+
def __init__(self):
|
|
426
|
+
self.logs = []
|
|
427
|
+
self.max_logs = 10000
|
|
428
|
+
self.lock = threading.RLock()
|
|
429
|
+
|
|
430
|
+
def log_security_event(self, request, event: str, details: Any = None):
|
|
431
|
+
"""Log security event"""
|
|
432
|
+
|
|
433
|
+
with self.lock:
|
|
434
|
+
log_entry = {
|
|
435
|
+
'timestamp': time.time(),
|
|
436
|
+
'event': event,
|
|
437
|
+
'ip': self._get_client_ip(request),
|
|
438
|
+
'path': getattr(request, 'path', ''),
|
|
439
|
+
'method': getattr(request, 'method', 'GET'),
|
|
440
|
+
'user_agent': getattr(request, 'headers', {}).get('user-agent', ''),
|
|
441
|
+
'details': details,
|
|
442
|
+
'severity': self._get_severity(event)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
self.logs.append(log_entry)
|
|
446
|
+
|
|
447
|
+
# Trim logs if needed
|
|
448
|
+
if len(self.logs) > self.max_logs:
|
|
449
|
+
self.logs = self.logs[-self.max_logs//2:]
|
|
450
|
+
|
|
451
|
+
def log_request(self, request, status: str):
|
|
452
|
+
"""Log regular request"""
|
|
453
|
+
|
|
454
|
+
with self.lock:
|
|
455
|
+
log_entry = {
|
|
456
|
+
'timestamp': time.time(),
|
|
457
|
+
'event': 'request',
|
|
458
|
+
'status': status,
|
|
459
|
+
'ip': self._get_client_ip(request),
|
|
460
|
+
'path': getattr(request, 'path', ''),
|
|
461
|
+
'method': getattr(request, 'method', 'GET'),
|
|
462
|
+
'severity': 'info'
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
self.logs.append(log_entry)
|
|
466
|
+
|
|
467
|
+
def get_recent_logs(self, limit: int = 100) -> List[Dict[str, Any]]:
|
|
468
|
+
"""Get recent log entries"""
|
|
469
|
+
|
|
470
|
+
with self.lock:
|
|
471
|
+
return list(reversed(self.logs[-limit:]))
|
|
472
|
+
|
|
473
|
+
def _get_client_ip(self, request) -> str:
|
|
474
|
+
"""Extract client IP from request"""
|
|
475
|
+
client = getattr(request, 'client', {})
|
|
476
|
+
return client.get('host', '127.0.0.1')
|
|
477
|
+
|
|
478
|
+
def _get_severity(self, event: str) -> str:
|
|
479
|
+
"""Determine event severity"""
|
|
480
|
+
|
|
481
|
+
if any(keyword in event.lower() for keyword in ['attack', 'injection', 'threat']):
|
|
482
|
+
return 'critical'
|
|
483
|
+
elif any(keyword in event.lower() for keyword in ['failed', 'invalid', 'blocked']):
|
|
484
|
+
return 'warning'
|
|
485
|
+
else:
|
|
486
|
+
return 'info'
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
class SecurityMetrics:
|
|
490
|
+
"""Security metrics tracking"""
|
|
491
|
+
|
|
492
|
+
def __init__(self):
|
|
493
|
+
self.requests_processed = 0
|
|
494
|
+
self.requests_blocked = 0
|
|
495
|
+
self.threats_detected = 0
|
|
496
|
+
self.errors_occurred = 0
|
|
497
|
+
self.start_time = time.time()
|
|
498
|
+
self.lock = threading.RLock()
|
|
499
|
+
|
|
500
|
+
def record_request(self):
|
|
501
|
+
"""Record successful request"""
|
|
502
|
+
with self.lock:
|
|
503
|
+
self.requests_processed += 1
|
|
504
|
+
|
|
505
|
+
def record_blocked(self):
|
|
506
|
+
"""Record blocked request"""
|
|
507
|
+
with self.lock:
|
|
508
|
+
self.requests_blocked += 1
|
|
509
|
+
|
|
510
|
+
def record_threat(self):
|
|
511
|
+
"""Record detected threat"""
|
|
512
|
+
with self.lock:
|
|
513
|
+
self.threats_detected += 1
|
|
514
|
+
|
|
515
|
+
def record_error(self):
|
|
516
|
+
"""Record security error"""
|
|
517
|
+
with self.lock:
|
|
518
|
+
self.errors_occurred += 1
|
|
519
|
+
|
|
520
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
521
|
+
"""Get security statistics"""
|
|
522
|
+
|
|
523
|
+
with self.lock:
|
|
524
|
+
uptime = time.time() - self.start_time
|
|
525
|
+
total_requests = self.requests_processed + self.requests_blocked
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
'uptime_seconds': uptime,
|
|
529
|
+
'requests_processed': self.requests_processed,
|
|
530
|
+
'requests_blocked': self.requests_blocked,
|
|
531
|
+
'threats_detected': self.threats_detected,
|
|
532
|
+
'errors_occurred': self.errors_occurred,
|
|
533
|
+
'total_requests': total_requests,
|
|
534
|
+
'block_rate': (self.requests_blocked / max(1, total_requests)) * 100,
|
|
535
|
+
'threat_rate': (self.threats_detected / max(1, total_requests)) * 100,
|
|
536
|
+
'requests_per_second': total_requests / max(1, uptime),
|
|
537
|
+
'security_effectiveness': max(0, 100 - (self.errors_occurred / max(1, total_requests)) * 100)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
# Import protection modules
|
|
542
|
+
class RateLimiter:
|
|
543
|
+
"""Simple rate limiter implementation"""
|
|
544
|
+
|
|
545
|
+
def __init__(self, max_requests=100, window_seconds=60):
|
|
546
|
+
self.max_requests = max_requests
|
|
547
|
+
self.window_seconds = window_seconds
|
|
548
|
+
self.requests = defaultdict(list)
|
|
549
|
+
self.lock = threading.RLock()
|
|
550
|
+
|
|
551
|
+
async def check_request(self, request) -> bool:
|
|
552
|
+
"""Check if request is within rate limits"""
|
|
553
|
+
|
|
554
|
+
client_ip = self._get_client_ip(request)
|
|
555
|
+
current_time = time.time()
|
|
556
|
+
|
|
557
|
+
with self.lock:
|
|
558
|
+
# Clean old requests
|
|
559
|
+
self.requests[client_ip] = [
|
|
560
|
+
timestamp for timestamp in self.requests[client_ip]
|
|
561
|
+
if current_time - timestamp < self.window_seconds
|
|
562
|
+
]
|
|
563
|
+
|
|
564
|
+
# Check rate limit
|
|
565
|
+
if len(self.requests[client_ip]) >= self.max_requests:
|
|
566
|
+
return False
|
|
567
|
+
|
|
568
|
+
# Record request
|
|
569
|
+
self.requests[client_ip].append(current_time)
|
|
570
|
+
return True
|
|
571
|
+
|
|
572
|
+
def _get_client_ip(self, request) -> str:
|
|
573
|
+
"""Extract client IP from request"""
|
|
574
|
+
client = getattr(request, 'client', {})
|
|
575
|
+
return client.get('host', '127.0.0.1')
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class CSRFProtection:
|
|
579
|
+
"""CSRF protection implementation"""
|
|
580
|
+
|
|
581
|
+
def __init__(self, secret_key: str):
|
|
582
|
+
self.secret_key = secret_key
|
|
583
|
+
|
|
584
|
+
async def verify_token(self, request) -> bool:
|
|
585
|
+
"""Verify CSRF token"""
|
|
586
|
+
|
|
587
|
+
# In production, implement proper CSRF token verification
|
|
588
|
+
return True
|
|
589
|
+
|
|
590
|
+
def generate_token(self, session_id: str) -> str:
|
|
591
|
+
"""Generate CSRF token"""
|
|
592
|
+
timestamp = str(int(time.time()))
|
|
593
|
+
message = f"{session_id}:{timestamp}"
|
|
594
|
+
signature = hmac.new(
|
|
595
|
+
self.secret_key.encode(),
|
|
596
|
+
message.encode(),
|
|
597
|
+
hashlib.sha256
|
|
598
|
+
).hexdigest()
|
|
599
|
+
return f"{timestamp}:{signature}"
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
class XSSProtection:
|
|
603
|
+
"""XSS protection implementation"""
|
|
604
|
+
|
|
605
|
+
def __init__(self):
|
|
606
|
+
self.dangerous_patterns = [
|
|
607
|
+
r'<script.*?>.*?</script>',
|
|
608
|
+
r'javascript:',
|
|
609
|
+
r'on\w+\s*=',
|
|
610
|
+
r'<iframe.*?>',
|
|
611
|
+
r'<object.*?>',
|
|
612
|
+
r'<embed.*?>'
|
|
613
|
+
]
|
|
614
|
+
|
|
615
|
+
async def check_request(self, request) -> bool:
|
|
616
|
+
"""Check request for XSS attempts"""
|
|
617
|
+
|
|
618
|
+
# Check URL path
|
|
619
|
+
path = getattr(request, 'path', '')
|
|
620
|
+
if self._contains_xss(path):
|
|
621
|
+
return False
|
|
622
|
+
|
|
623
|
+
# Check query parameters
|
|
624
|
+
query_params = getattr(request, 'query_params', {})
|
|
625
|
+
for key, value in query_params.items():
|
|
626
|
+
if self._contains_xss(f"{key}={value}"):
|
|
627
|
+
return False
|
|
628
|
+
|
|
629
|
+
return True
|
|
630
|
+
|
|
631
|
+
def sanitize_output(self, content: Union[str, bytes]) -> Union[str, bytes]:
|
|
632
|
+
"""Sanitize output content"""
|
|
633
|
+
|
|
634
|
+
if isinstance(content, bytes):
|
|
635
|
+
content_str = content.decode('utf-8', errors='ignore')
|
|
636
|
+
sanitized = self._sanitize_string(content_str)
|
|
637
|
+
return sanitized.encode('utf-8')
|
|
638
|
+
elif isinstance(content, str):
|
|
639
|
+
return self._sanitize_string(content)
|
|
640
|
+
|
|
641
|
+
return content
|
|
642
|
+
|
|
643
|
+
def _contains_xss(self, content: str) -> bool:
|
|
644
|
+
"""Check if content contains XSS patterns"""
|
|
645
|
+
|
|
646
|
+
content_lower = content.lower()
|
|
647
|
+
for pattern in self.dangerous_patterns:
|
|
648
|
+
if re.search(pattern, content_lower, re.IGNORECASE):
|
|
649
|
+
return True
|
|
650
|
+
|
|
651
|
+
return False
|
|
652
|
+
|
|
653
|
+
def _sanitize_string(self, content: str) -> str:
|
|
654
|
+
"""Sanitize string content"""
|
|
655
|
+
|
|
656
|
+
# Basic HTML entity encoding
|
|
657
|
+
content = content.replace('&', '&')
|
|
658
|
+
content = content.replace('<', '<')
|
|
659
|
+
content = content.replace('>', '>')
|
|
660
|
+
content = content.replace('"', '"')
|
|
661
|
+
content = content.replace("'", ''')
|
|
662
|
+
|
|
663
|
+
return content
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
class SQLInjectionProtection:
|
|
667
|
+
"""SQL injection protection"""
|
|
668
|
+
|
|
669
|
+
def __init__(self):
|
|
670
|
+
self.sql_patterns = [
|
|
671
|
+
r'(\bunion\b.*\bselect\b)',
|
|
672
|
+
r'(\bdrop\b.*\btable\b)',
|
|
673
|
+
r'(\binsert\b.*\binto\b)',
|
|
674
|
+
r'(\bdelete\b.*\bfrom\b)',
|
|
675
|
+
r'(\bupdate\b.*\bset\b)',
|
|
676
|
+
r'(\bor\b.*1\s*=\s*1)',
|
|
677
|
+
r'(\band\b.*1\s*=\s*1)',
|
|
678
|
+
r'(exec\s*\(|execute\s*\()',
|
|
679
|
+
r'(sp_\w+)',
|
|
680
|
+
r'(xp_\w+)'
|
|
681
|
+
]
|
|
682
|
+
|
|
683
|
+
async def check_request(self, request) -> bool:
|
|
684
|
+
"""Check request for SQL injection attempts"""
|
|
685
|
+
|
|
686
|
+
# Check URL path
|
|
687
|
+
path = getattr(request, 'path', '')
|
|
688
|
+
if self._contains_sql_injection(path):
|
|
689
|
+
return False
|
|
690
|
+
|
|
691
|
+
# Check query parameters
|
|
692
|
+
query_params = getattr(request, 'query_params', {})
|
|
693
|
+
for key, value in query_params.items():
|
|
694
|
+
if self._contains_sql_injection(f"{key}={value}"):
|
|
695
|
+
return False
|
|
696
|
+
|
|
697
|
+
return True
|
|
698
|
+
|
|
699
|
+
def _contains_sql_injection(self, content: str) -> bool:
|
|
700
|
+
"""Check if content contains SQL injection patterns"""
|
|
701
|
+
|
|
702
|
+
content_lower = content.lower()
|
|
703
|
+
for pattern in self.sql_patterns:
|
|
704
|
+
if re.search(pattern, content_lower, re.IGNORECASE):
|
|
705
|
+
return True
|
|
706
|
+
|
|
707
|
+
return False
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
class InputValidator:
|
|
711
|
+
"""Input validation system"""
|
|
712
|
+
|
|
713
|
+
def __init__(self):
|
|
714
|
+
self.max_input_length = 10000
|
|
715
|
+
self.forbidden_chars = ['\x00', '\x01', '\x02', '\x03', '\x04', '\x05']
|
|
716
|
+
|
|
717
|
+
async def validate_request(self, request) -> bool:
|
|
718
|
+
"""Validate request input"""
|
|
719
|
+
|
|
720
|
+
# Check content length
|
|
721
|
+
if hasattr(request, 'headers'):
|
|
722
|
+
content_length = request.headers.get('content-length', '0')
|
|
723
|
+
try:
|
|
724
|
+
length = int(content_length)
|
|
725
|
+
if length > self.max_input_length:
|
|
726
|
+
return False
|
|
727
|
+
except ValueError:
|
|
728
|
+
pass
|
|
729
|
+
|
|
730
|
+
# Check for null bytes and control characters
|
|
731
|
+
path = getattr(request, 'path', '')
|
|
732
|
+
if any(char in path for char in self.forbidden_chars):
|
|
733
|
+
return False
|
|
734
|
+
|
|
735
|
+
return True
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
# Global security manager instance
|
|
739
|
+
_security_manager = None
|
|
740
|
+
|
|
741
|
+
def get_security_manager() -> SecurityManager:
|
|
742
|
+
"""Get global security manager instance"""
|
|
743
|
+
global _security_manager
|
|
744
|
+
if _security_manager is None:
|
|
745
|
+
_security_manager = SecurityManager()
|
|
746
|
+
return _security_manager
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
def secure_middleware():
|
|
750
|
+
"""Middleware for automatic security protection"""
|
|
751
|
+
|
|
752
|
+
def middleware(app):
|
|
753
|
+
security_manager = get_security_manager()
|
|
754
|
+
original_call = app.__call__
|
|
755
|
+
|
|
756
|
+
async def secured_app(scope, receive, send):
|
|
757
|
+
# Create request object
|
|
758
|
+
request = type('Request', (), {
|
|
759
|
+
'scope': scope,
|
|
760
|
+
'path': scope.get('path', '/'),
|
|
761
|
+
'method': scope.get('method', 'GET'),
|
|
762
|
+
'headers': dict(scope.get('headers', [])),
|
|
763
|
+
'query_params': {},
|
|
764
|
+
'client': scope.get('client', {'host': '127.0.0.1'})
|
|
765
|
+
})()
|
|
766
|
+
|
|
767
|
+
# Process through security layers
|
|
768
|
+
async def response_callback():
|
|
769
|
+
return await original_call(scope, receive, send)
|
|
770
|
+
|
|
771
|
+
try:
|
|
772
|
+
result = await security_manager.process_request(request, response_callback)
|
|
773
|
+
|
|
774
|
+
if isinstance(result, dict) and 'status_code' in result:
|
|
775
|
+
# Security blocked the request
|
|
776
|
+
await send({
|
|
777
|
+
'type': 'http.response.start',
|
|
778
|
+
'status': result['status_code'],
|
|
779
|
+
'headers': [[k.encode(), v.encode()] for k, v in result['headers'].items()]
|
|
780
|
+
})
|
|
781
|
+
|
|
782
|
+
await send({
|
|
783
|
+
'type': 'http.response.body',
|
|
784
|
+
'body': result['body'].encode() if isinstance(result['body'], str) else result['body']
|
|
785
|
+
})
|
|
786
|
+
else:
|
|
787
|
+
# Request was processed normally
|
|
788
|
+
return result
|
|
789
|
+
|
|
790
|
+
except Exception as e:
|
|
791
|
+
# Security error - return safe error response
|
|
792
|
+
error_response = {
|
|
793
|
+
'error': 'Security error occurred',
|
|
794
|
+
'framework': 'CREATESONLINE',
|
|
795
|
+
'protected': True
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
await send({
|
|
799
|
+
'type': 'http.response.start',
|
|
800
|
+
'status': 500,
|
|
801
|
+
'headers': [[b'content-type', b'application/json']]
|
|
802
|
+
})
|
|
803
|
+
|
|
804
|
+
await send({
|
|
805
|
+
'type': 'http.response.body',
|
|
806
|
+
'body': json.dumps(error_response).encode()
|
|
807
|
+
})
|
|
808
|
+
|
|
809
|
+
return secured_app
|
|
810
|
+
|
|
811
|
+
return middleware
|