GuardianUnivalle-Benito-Yucra 0.1.44__tar.gz → 0.1.46__tar.gz

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.

Potentially problematic release.


This version of GuardianUnivalle-Benito-Yucra might be problematic. Click here for more details.

Files changed (25) hide show
  1. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py +36 -65
  2. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py +78 -37
  3. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/PKG-INFO +1 -1
  4. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/PKG-INFO +1 -1
  5. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/pyproject.toml +1 -1
  6. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/__init__.py +0 -0
  7. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py +0 -0
  8. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py +0 -0
  9. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py +0 -0
  10. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/criptografia/kdf.py +0 -0
  11. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py +0 -0
  12. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py +0 -0
  13. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py +0 -0
  14. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py +0 -0
  15. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/mitigacion/limitador_peticion.py +0 -0
  16. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/mitigacion/lista_bloqueo.py +0 -0
  17. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/puntuacion/puntuacion_amenaza.py +0 -0
  18. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra/utilidades.py +0 -0
  19. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/SOURCES.txt +0 -0
  20. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/dependency_links.txt +0 -0
  21. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/requires.txt +0 -0
  22. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/GuardianUnivalle_Benito_Yucra.egg-info/top_level.txt +0 -0
  23. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/LICENSE +0 -0
  24. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/README.md +0 -0
  25. {guardianunivalle_benito_yucra-0.1.44 → guardianunivalle_benito_yucra-0.1.46}/setup.cfg +0 -0
@@ -1,4 +1,4 @@
1
-
1
+ # CSRF defense (parche recomendado)
2
2
  from __future__ import annotations
3
3
  import secrets
4
4
  import logging
@@ -9,9 +9,6 @@ from urllib.parse import urlparse
9
9
  from django.conf import settings
10
10
  from django.utils.deprecation import MiddlewareMixin
11
11
 
12
- # ======================================================
13
- # === CONFIGURACIÓN DE LOGGER ===
14
- # ======================================================
15
12
  logger = logging.getLogger("csrfdefense")
16
13
  logger.setLevel(logging.INFO)
17
14
  if not logger.handlers:
@@ -19,52 +16,34 @@ if not logger.handlers:
19
16
  handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
20
17
  logger.addHandler(handler)
21
18
 
22
- # ======================================================
23
- # === FUNCIONES AUXILIARES DE TOKEN CSRF ===
24
- # ======================================================
25
- def registrar_evento(tipo: str, mensaje: str):
26
- """Registra eventos importantes en los logs."""
27
- logger.warning(f"[{tipo}] {mensaje}")
28
-
29
- def generar_token_csrf() -> str:
30
- """Genera un token CSRF seguro."""
31
- token = secrets.token_hex(32)
32
- registrar_evento("CSRF", "Token CSRF generado")
33
- return token
34
-
35
- def validar_token_csrf(token: str, token_sesion: str) -> bool:
36
- """Valida que el token recibido coincida con el token en sesión."""
37
- valido = token == token_sesion
38
- if not valido:
39
- registrar_evento("CSRF", "Intento de CSRF detectado (token no coincide)")
40
- return valido
41
-
42
- # ======================================================
43
- # === CONSTANTES Y CONFIGURACIONES ===
44
- # ======================================================
45
19
  STATE_CHANGING_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
46
20
  CSRF_HEADER_NAMES = ("HTTP_X_CSRFTOKEN", "HTTP_X_CSRF_TOKEN")
47
21
  CSRF_COOKIE_NAME = getattr(settings, "CSRF_COOKIE_NAME", "csrftoken")
48
22
  POST_FIELD_NAME = "csrfmiddlewaretoken"
49
23
 
50
- # Content-Type realmente sospechosos
24
+ # Nota: NO consideramos 'application/json' sospechoso aquí por defecto,
25
+ # porque muchas APIs legítimas usan JSON.
51
26
  SUSPICIOUS_CT_PATTERNS = [
52
27
  re.compile(r"text/plain", re.I),
53
- re.compile(r"application/json", re.I),
28
+ re.compile(r"application/x-www-form-urlencoded", re.I),
29
+ re.compile(r"multipart/form-data", re.I),
54
30
  ]
55
31
 
56
- # ======================================================
57
- # === FUNCIONES DE APOYO ===
58
- # ======================================================
32
+ # Umbral minimo de "señales" para marcar como ataque (configurable)
33
+ CSRF_DEFENSE_MIN_SIGNALS = getattr(settings, "CSRF_DEFENSE_MIN_SIGNALS", 1)
34
+ # Opción para excluir rutas de API que manejan JSON (cambia según tu proyecto)
35
+ CSRF_DEFENSE_EXCLUDED_API_PREFIXES = getattr(settings, "CSRF_DEFENSE_EXCLUDED_API_PREFIXES", ["/api/"])
36
+
59
37
  def get_client_ip(request):
60
- """Obtiene la IP real del cliente."""
61
38
  x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
62
39
  if x_forwarded_for:
63
- return x_forwarded_for.split(",")[0].strip()
40
+ # toma la primera IP real
41
+ ips = [ip.strip() for ip in x_forwarded_for.split(",") if ip.strip()]
42
+ if ips:
43
+ return ips[0]
64
44
  return request.META.get("REMOTE_ADDR", "")
65
45
 
66
46
  def host_from_header(header_value: str) -> str | None:
67
- """Extrae el host limpio desde una cabecera."""
68
47
  if not header_value:
69
48
  return None
70
49
  try:
@@ -76,53 +55,43 @@ def host_from_header(header_value: str) -> str | None:
76
55
  return None
77
56
 
78
57
  def origin_matches_host(request) -> bool:
79
- """Verifica si Origin/Referer coinciden con Host."""
80
58
  host_header = request.META.get("HTTP_HOST") or request.META.get("SERVER_NAME")
81
59
  if not host_header:
82
60
  return True
83
-
84
61
  host = host_header.split(":")[0]
85
62
  origin = request.META.get("HTTP_ORIGIN", "")
86
63
  referer = request.META.get("HTTP_REFERER", "")
87
-
88
64
  origin_host = host_from_header(origin)
89
65
  referer_host = host_from_header(referer)
90
-
91
- # Detectar valores con scripts o URLs maliciosas
92
- if any(re.search(r"(javascript:|<script|data:text/html)", h or "", re.I)
93
- for h in [origin, referer]):
66
+ # bloquear obvious javascript: referers
67
+ if any(re.search(r"(javascript:|<script|data:text/html)", h or "", re.I) for h in [origin, referer]):
94
68
  return False
95
-
96
69
  if origin_host and origin_host == host:
97
70
  return True
98
71
  if referer_host and referer_host == host:
99
72
  return True
73
+ # si no hay origin ni referer, lo consideramos neutral (no marcar)
100
74
  if not origin and not referer:
101
75
  return True
102
-
103
76
  return False
104
77
 
105
78
  def has_csrf_token(request) -> bool:
106
- """Comprueba si hay un token CSRF presente."""
79
+ # busca header, cookie o campo form
107
80
  for h in CSRF_HEADER_NAMES:
108
81
  if request.META.get(h):
109
82
  return True
110
-
111
83
  cookie_val = request.COOKIES.get(CSRF_COOKIE_NAME)
112
84
  if cookie_val:
113
85
  return True
114
-
115
86
  try:
116
87
  if request.method == "POST" and hasattr(request, "POST"):
117
88
  if request.POST.get(POST_FIELD_NAME):
118
89
  return True
119
90
  except Exception:
120
91
  pass
121
-
122
92
  return False
123
93
 
124
94
  def extract_payload_text(request) -> str:
125
- """Extrae el cuerpo útil de la solicitud."""
126
95
  parts: List[str] = []
127
96
  try:
128
97
  body = request.body.decode("utf-8", errors="ignore")
@@ -137,17 +106,15 @@ def extract_payload_text(request) -> str:
137
106
  parts.append(request.META.get("HTTP_REFERER", ""))
138
107
  return " ".join([p for p in parts if p])
139
108
 
140
- # ======================================================
141
- # === MIDDLEWARE DE DEFENSA CSRF ===
142
- # ======================================================
143
109
  class CSRFDefenseMiddleware(MiddlewareMixin):
144
- """
145
- Middleware para DETECTAR intentos de CSRF:
146
- - Registra request.csrf_attack_info con 'tipos': ['CSRF'] y 'descripcion' con las razones.
147
- - No bloquea directamente: deja que AuditoriaMiddleware lo maneje.
148
- """
149
-
150
110
  def process_request(self, request):
111
+ # 1) Excluir APIs JSON si se configuró así
112
+ for prefix in CSRF_DEFENSE_EXCLUDED_API_PREFIXES:
113
+ if request.path.startswith(prefix):
114
+ # debug log opcional
115
+ logger.debug(f"[CSRFDefense] Skip analysis for API prefix {prefix} path {request.path}")
116
+ return None
117
+
151
118
  client_ip = get_client_ip(request)
152
119
  trusted_ips = getattr(settings, "CSRF_DEFENSE_TRUSTED_IPS", [])
153
120
  if client_ip in trusted_ips:
@@ -172,20 +139,20 @@ class CSRFDefenseMiddleware(MiddlewareMixin):
172
139
  if not origin_matches_host(request):
173
140
  descripcion.append("Origin/Referer no coinciden con Host (posible cross-site)")
174
141
 
175
- # 3) Content-Type sospechoso
176
- content_type = request.META.get("CONTENT_TYPE", "") or ""
142
+ # 3) Content-Type sospechoso (solo marcaremos si coincide uno de los patterns)
143
+ content_type = (request.META.get("CONTENT_TYPE") or "")
177
144
  for patt in SUSPICIOUS_CT_PATTERNS:
178
145
  if patt.search(content_type):
179
146
  descripcion.append(f"Content-Type sospechoso: {content_type}")
180
147
  break
181
148
 
182
- # 4) Referer ausente y sin token
149
+ # 4) Referer ausente y sin header CSRF
183
150
  referer = request.META.get("HTTP_REFERER", "")
184
151
  if not referer and not any(request.META.get(h) for h in CSRF_HEADER_NAMES):
185
152
  descripcion.append("Referer ausente y sin X-CSRFToken")
186
153
 
187
- # === Registro del ataque detectado ===
188
- if descripcion:
154
+ # Si señales >= umbral entonces marcamos para auditoría
155
+ if descripcion and len(descripcion) >= CSRF_DEFENSE_MIN_SIGNALS:
189
156
  w_csrf = getattr(settings, "CSRF_DEFENSE_WEIGHT", 0.2)
190
157
  intentos_csrf = len(descripcion)
191
158
  s_csrf = w_csrf * intentos_csrf
@@ -199,9 +166,13 @@ class CSRFDefenseMiddleware(MiddlewareMixin):
199
166
  }
200
167
 
201
168
  logger.warning(
202
- "CSRF detectado desde IP %s: %s ; payload: %.200s ; score: %.2f",
203
- client_ip, descripcion, payload, s_csrf,
169
+ "CSRF detectado desde IP %s: %s ; path=%s ; Content-Type=%s ; score=%.2f",
170
+ client_ip, descripcion, request.path, content_type, s_csrf
204
171
  )
172
+ else:
173
+ # debug útil: saber por qué NO se marcó
174
+ if descripcion:
175
+ logger.debug(f"[CSRFDefense] low-signals ({len(descripcion)}) not marking: {descripcion}")
205
176
 
206
177
  return None
207
178
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
  import time
3
3
  import logging
4
4
  import json
5
+ from collections import deque
5
6
  from typing import Dict, List
6
7
  from django.conf import settings
7
8
  from django.utils.deprecation import MiddlewareMixin
@@ -9,7 +10,7 @@ from ..mitigacion.limitador_peticion import limitar_peticion
9
10
  from ..auditoria.registro_auditoria import registrar_evento
10
11
 
11
12
  # =====================================================
12
- # === CONFIGURACIÓN DEL LOGGER ===
13
+ # === CONFIGURACIÓN DEL LOGGER ===
13
14
  # =====================================================
14
15
  logger = logging.getLogger("dosdefense")
15
16
  logger.setLevel(logging.INFO)
@@ -19,108 +20,148 @@ if not logger.handlers:
19
20
  logger.addHandler(handler)
20
21
 
21
22
  # =====================================================
22
- # === PARÁMETROS DE CONFIGURACIÓN BASE ===
23
+ # === PARÁMETROS DE CONFIGURACIÓN BASE ===
23
24
  # =====================================================
24
25
  LIMITE_PETICIONES = getattr(settings, "DOS_LIMITE_PETICIONES", 100) # por minuto
25
26
  VENTANA_SEGUNDOS = getattr(settings, "DOS_VENTANA_SEGUNDOS", 60)
26
27
  PESO_DOS = getattr(settings, "DOS_PESO", 0.6)
28
+ LIMITE_ENDPOINTS_DISTINTOS = getattr(settings, "DOS_LIMITE_ENDPOINTS", 50)
29
+ TRUSTED_IPS = getattr(settings, "DOS_TRUSTED_IPS", [])
27
30
 
28
31
  # =====================================================
29
- # === REGISTRO TEMPORAL DE SOLICITUDES POR IP ===
32
+ # === REGISTRO TEMPORAL EN MEMORIA ===
30
33
  # =====================================================
31
- # En producción se recomienda Redis o Memcached
32
- REGISTRO_SOLICITUDES: Dict[str, List[float]] = {}
33
-
34
+ # Estructura: { ip: deque([timestamps]), ... }
35
+ # deque es eficiente para ventanas deslizantes
36
+ REGISTRO_SOLICITUDES: Dict[str, deque] = {}
37
+ REGISTRO_ENDPOINTS: Dict[str, set] = {}
34
38
 
35
39
  # =====================================================
36
- # === FUNCIONES AUXILIARES ===
40
+ # === FUNCIONES AUXILIARES ===
37
41
  # =====================================================
38
42
  def get_client_ip(request) -> str:
39
43
  """Obtiene la IP real del cliente (considera proxies)."""
40
44
  x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
41
45
  if x_forwarded_for:
42
46
  return x_forwarded_for.split(",")[0].strip()
43
- return request.META.get("REMOTE_ADDR", "")
47
+ return request.META.get("REMOTE_ADDR", "") or "0.0.0.0"
48
+
49
+
50
+ def limpiar_registro_global():
51
+ """Elimina IPs sin actividad reciente para evitar uso excesivo de memoria."""
52
+ ahora = time.time()
53
+ expiracion = VENTANA_SEGUNDOS * 2 # doble ventana
54
+ inactivas = [
55
+ ip for ip, tiempos in REGISTRO_SOLICITUDES.items()
56
+ if tiempos and ahora - tiempos[-1] > expiracion
57
+ ]
58
+ for ip in inactivas:
59
+ REGISTRO_SOLICITUDES.pop(ip, None)
60
+ REGISTRO_ENDPOINTS.pop(ip, None)
44
61
 
45
62
 
46
63
  def limpiar_registro(ip: str):
47
64
  """Limpia peticiones antiguas fuera de la ventana de tiempo."""
48
65
  ahora = time.time()
49
- REGISTRO_SOLICITUDES[ip] = [
50
- t for t in REGISTRO_SOLICITUDES.get(ip, []) if ahora - t < VENTANA_SEGUNDOS
51
- ]
66
+ if ip not in REGISTRO_SOLICITUDES:
67
+ REGISTRO_SOLICITUDES[ip] = deque()
68
+ tiempos = REGISTRO_SOLICITUDES[ip]
69
+ while tiempos and ahora - tiempos[0] > VENTANA_SEGUNDOS:
70
+ tiempos.popleft()
52
71
 
53
72
 
54
- def calcular_nivel_amenaza_dos(
55
- tasa_peticion: int, limite: int = LIMITE_PETICIONES
56
- ) -> float:
73
+ def calcular_nivel_amenaza_dos(tasa_peticion: int, limite: int = LIMITE_PETICIONES) -> float:
57
74
  """
58
- Calcula la puntuación de amenaza DoS basada en el peso configurado.
75
+ Calcula la puntuación de amenaza DoS.
59
76
  Fórmula: S_dos = w_dos * (tasa_peticion / limite)
60
77
  """
61
- proporcion = tasa_peticion / limite
62
- s_dos = PESO_DOS * proporcion
78
+ proporcion = tasa_peticion / max(limite, 1)
79
+ s_dos = PESO_DOS * min(proporcion, 2.0)
63
80
  return round(min(s_dos, 1.0), 3)
64
81
 
65
82
 
66
83
  def detectar_dos(ip: str, tasa_peticion: int, limite: int = LIMITE_PETICIONES) -> bool:
67
84
  """Evalúa si la tasa de peticiones excede el umbral permitido."""
68
85
  if tasa_peticion > limite:
69
- # Registrar evento en auditoría
70
86
  registrar_evento(
71
87
  tipo="DoS",
72
88
  descripcion=f"Alta tasa de peticiones desde {ip}: {tasa_peticion} req/min (límite {limite})",
73
89
  severidad="ALTA",
74
90
  )
75
- # Mitigación: pasar un usuario genérico
76
91
  limitar_peticion(usuario_id="anonimo")
77
92
  return True
93
+ elif tasa_peticion > limite * 0.75:
94
+ # Umbral de advertencia
95
+ registrar_evento(
96
+ tipo="DoS",
97
+ descripcion=f"Posible saturación desde {ip}: {tasa_peticion} req/min",
98
+ severidad="MEDIA",
99
+ )
78
100
  return False
79
101
 
80
102
 
103
+ def analizar_headers(user_agent: str, referer: str) -> List[str]:
104
+ """Detecta patrones de agentes o cabeceras sospechosas."""
105
+ sospechas = []
106
+ if not user_agent or len(user_agent) < 10:
107
+ sospechas.append("User-Agent vacío o anómalo")
108
+ if "curl" in user_agent.lower() or "python" in user_agent.lower():
109
+ sospechas.append("User-Agent indica script o bot")
110
+ if referer and any(palabra in referer.lower() for palabra in ["attack", "scan", "bot"]):
111
+ sospechas.append("Referer sospechoso")
112
+ return sospechas
113
+
114
+
81
115
  # =====================================================
82
- # === MIDDLEWARE DE DETECCIÓN DE DoS ===
116
+ # === MIDDLEWARE PRINCIPAL DE DEFENSA DoS ===
83
117
  # =====================================================
84
118
  class DOSDefenseMiddleware(MiddlewareMixin):
85
119
  """
86
- Middleware para detección y registro de ataques DoS.
87
- - Captura IP, agente y cabeceras sospechosas.
88
- - Evalúa la frecuencia de peticiones por IP.
89
- - Marca request.dos_attack_info con información del intento.
120
+ Middleware de detección y registro de ataques DoS.
90
121
  """
91
122
 
92
123
  def process_request(self, request):
124
+ limpiar_registro_global()
125
+
93
126
  client_ip = get_client_ip(request)
127
+ if client_ip in TRUSTED_IPS:
128
+ return None
129
+
94
130
  user_agent = request.META.get("HTTP_USER_AGENT", "Desconocido")
95
131
  referer = request.META.get("HTTP_REFERER", "")
96
132
  path = request.path
97
133
 
98
- # Limpieza de registro anterior
134
+ # Registrar endpoint accedido
135
+ REGISTRO_ENDPOINTS.setdefault(client_ip, set()).add(path)
136
+
137
+ # Mantener ventana deslizante
99
138
  limpiar_registro(client_ip)
139
+ REGISTRO_SOLICITUDES[client_ip].append(time.time())
100
140
 
101
- # Registrar nueva petición
102
- REGISTRO_SOLICITUDES.setdefault(client_ip, []).append(time.time())
103
141
  tasa = len(REGISTRO_SOLICITUDES[client_ip])
104
142
  nivel = calcular_nivel_amenaza_dos(tasa)
105
143
  es_dos = detectar_dos(client_ip, tasa)
106
144
 
107
- if es_dos:
108
- descripcion = [
109
- f"Tasa de {tasa} req/min excede límite {LIMITE_PETICIONES}",
110
- f"User-Agent: {user_agent}",
111
- f"Referer: {referer or 'N/A'}",
112
- f"Ruta: {path}",
113
- ]
145
+ descripcion = []
146
+ sospechas_headers = analizar_headers(user_agent, referer)
147
+ if sospechas_headers:
148
+ descripcion.extend(sospechas_headers)
149
+
150
+ # Verificar exceso de endpoints distintos (indicativo de escaneo)
151
+ if len(REGISTRO_ENDPOINTS[client_ip]) > LIMITE_ENDPOINTS_DISTINTOS:
152
+ descripcion.append("Número anormal de endpoints distintos accedidos")
153
+
154
+ if es_dos or descripcion:
155
+ descripcion.insert(0, f"Tasa actual: {tasa} req/min (nivel {nivel:.2f})")
156
+ descripcion.append(f"Ruta: {path}")
114
157
 
115
- # Log profesional
116
158
  logger.warning(
117
- "DoS detectado desde IP %s: %s ; nivel: %.2f",
159
+ "DoS detectado o sospechoso desde IP %s: %s ; nivel: %.2f",
118
160
  client_ip,
119
161
  descripcion,
120
162
  nivel,
121
163
  )
122
164
 
123
- # Enviar a sistema de auditoría
124
165
  request.dos_attack_info = {
125
166
  "ip": client_ip,
126
167
  "tipos": ["DoS"],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GuardianUnivalle-Benito-Yucra
3
- Version: 0.1.44
3
+ Version: 0.1.46
4
4
  Summary: Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS, Keylogger) para Django/Flask
5
5
  Author-email: Andres Benito Calle Yucra <benitoandrescalle035@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GuardianUnivalle-Benito-Yucra
3
- Version: 0.1.44
3
+ Version: 0.1.46
4
4
  Summary: Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS, Keylogger) para Django/Flask
5
5
  Author-email: Andres Benito Calle Yucra <benitoandrescalle035@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GuardianUnivalle-Benito-Yucra" # usar mayúsculas consistente
7
- version = "0.1.44"
7
+ version = "0.1.46"
8
8
  description = "Middleware y detectores de seguridad (SQLi, XSS, CSRF, DoS, Keylogger) para Django/Flask"
9
9
  authors = [
10
10
  { name = "Andres Benito Calle Yucra", email = "benitoandrescalle035@gmail.com" }