GuardianUnivalle-Benito-Yucra 0.1.45__tar.gz → 0.1.47__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 (27) hide show
  1. guardianunivalle_benito_yucra-0.1.47/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py +118 -0
  2. guardianunivalle_benito_yucra-0.1.47/GuardianUnivalle_Benito_Yucra/auditoria/utils_auditoria.py +57 -0
  3. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_dos.py +78 -37
  4. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/PKG-INFO +1 -1
  5. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/SOURCES.txt +1 -0
  6. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/PKG-INFO +1 -1
  7. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/pyproject.toml +1 -1
  8. guardianunivalle_benito_yucra-0.1.45/GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py +0 -40
  9. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/__init__.py +0 -0
  10. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py +0 -0
  11. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py +0 -0
  12. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/criptografia/kdf.py +0 -0
  13. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_csrf.py +0 -0
  14. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_keylogger.py +0 -0
  15. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_sql.py +0 -0
  16. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/detectores/detector_xss.py +0 -0
  17. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/middleware_web/middleware_web.py +0 -0
  18. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/mitigacion/limitador_peticion.py +0 -0
  19. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/mitigacion/lista_bloqueo.py +0 -0
  20. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/puntuacion/puntuacion_amenaza.py +0 -0
  21. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra/utilidades.py +0 -0
  22. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/dependency_links.txt +0 -0
  23. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/requires.txt +0 -0
  24. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/GuardianUnivalle_Benito_Yucra.egg-info/top_level.txt +0 -0
  25. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/LICENSE +0 -0
  26. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/README.md +0 -0
  27. {guardianunivalle_benito_yucra-0.1.45 → guardianunivalle_benito_yucra-0.1.47}/setup.cfg +0 -0
@@ -0,0 +1,118 @@
1
+ import os
2
+ import datetime
3
+ import json
4
+ import platform
5
+ from django.utils import timezone
6
+
7
+ LOG_FILE = "auditoria_guardian.log"
8
+
9
+ # =====================================================
10
+ # === FUNCIONES DE CAPTURA Y ANÁLISIS DE CLIENTE ===
11
+ # =====================================================
12
+ def capturar_datos_cliente(request) -> dict:
13
+ """Extrae información útil del cliente que accede al sistema."""
14
+ ip = request.META.get("HTTP_X_FORWARDED_FOR")
15
+ if ip:
16
+ ip = ip.split(",")[0].strip()
17
+ else:
18
+ ip = request.META.get("REMOTE_ADDR", "Desconocida")
19
+
20
+ user_agent = request.META.get("HTTP_USER_AGENT", "Desconocido")
21
+ metodo = request.method
22
+ ruta = request.path
23
+ parametros = request.GET.dict() if metodo == "GET" else request.POST.dict()
24
+ so_servidor = platform.system()
25
+
26
+ return {
27
+ "ip_cliente": ip,
28
+ "navegador": user_agent,
29
+ "metodo": metodo,
30
+ "ruta": ruta,
31
+ "parametros": parametros,
32
+ "servidor_os": so_servidor,
33
+ "hora_servidor": timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
34
+ }
35
+
36
+
37
+ def analizar_comportamiento_cliente(datos_cliente: dict) -> tuple[str, str]:
38
+ """
39
+ Aplica reglas básicas de detección:
40
+ - IP sospechosa o repetitiva
41
+ - Agente extraño o vacío
42
+ - Peticiones sospechosas (ej: /admin, /etc/passwd)
43
+ Devuelve: (nivel_riesgo, descripcion)
44
+ """
45
+ descripcion = []
46
+ riesgo = "BAJO"
47
+
48
+ ip = datos_cliente.get("ip_cliente", "")
49
+ user_agent = datos_cliente.get("navegador", "").lower()
50
+ ruta = datos_cliente.get("ruta", "")
51
+
52
+ # === Reglas simples ===
53
+ if not user_agent or "curl" in user_agent or "python" in user_agent:
54
+ descripcion.append("Agente de usuario anómalo (posible bot o script).")
55
+ riesgo = "MEDIO"
56
+
57
+ if "admin" in ruta or "etc/passwd" in ruta or "../" in ruta:
58
+ descripcion.append("Ruta sospechosa accedida.")
59
+ riesgo = "ALTO"
60
+
61
+ if "Desconocida" in ip or ip.startswith("192.168.") is False:
62
+ descripcion.append(f"IP externa detectada: {ip}")
63
+ riesgo = "MEDIO"
64
+
65
+ # Comportamiento sin parámetros ni cabeceras
66
+ if not datos_cliente.get("parametros"):
67
+ descripcion.append("Petición sin parámetros ni cabeceras útiles.")
68
+ riesgo = "BAJO"
69
+
70
+ if not descripcion:
71
+ descripcion.append("Acceso normal detectado.")
72
+
73
+ return riesgo, " | ".join(descripcion)
74
+
75
+
76
+ # =====================================================
77
+ # === FUNCIÓN PRINCIPAL DE REGISTRO ===
78
+ # =====================================================
79
+ def registrar_evento(request, tipo: str = "ACCESO", extra: dict | None = None):
80
+ """Registra un evento de auditoría detallado del cliente."""
81
+ try:
82
+ datos_cliente = capturar_datos_cliente(request)
83
+ severidad, descripcion = analizar_comportamiento_cliente(datos_cliente)
84
+
85
+ evento = {
86
+ "fecha": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
87
+ "tipo": tipo,
88
+ "descripcion": descripcion,
89
+ "severidad": severidad,
90
+ "cliente": datos_cliente,
91
+ "extra": extra or {},
92
+ }
93
+
94
+ # Crear carpeta solo si hay directorio en la ruta
95
+ log_dir = os.path.dirname(LOG_FILE)
96
+ if log_dir:
97
+ os.makedirs(log_dir, exist_ok=True)
98
+
99
+ # Registrar en archivo
100
+ with open(LOG_FILE, "a", encoding="utf-8") as f:
101
+ f.write(json.dumps(evento, ensure_ascii=False) + "\n")
102
+
103
+ # (Opcional) Log en consola
104
+ print(f"[AUDITORÍA] Evento registrado: {evento['descripcion']} (nivel {severidad})")
105
+
106
+ except Exception as e:
107
+ print(f"[Auditoría] Error al registrar evento: {e}")
108
+
109
+
110
+ # =====================================================
111
+ # === CONSULTA DE REGISTROS ===
112
+ # =====================================================
113
+ def generar_reporte() -> str:
114
+ """Devuelve todo el contenido del archivo de auditoría."""
115
+ if not os.path.exists(LOG_FILE):
116
+ return "No hay registros aún."
117
+ with open(LOG_FILE, "r", encoding="utf-8") as f:
118
+ return f.read()
@@ -0,0 +1,57 @@
1
+ import requests
2
+ from django.utils.timezone import now
3
+ from user_agents import parse
4
+
5
+ def obtener_datos_maquina(request):
6
+ """Obtiene información detallada del cliente desde la petición"""
7
+ try:
8
+ # --- IP real ---
9
+ ip = (
10
+ request.META.get("HTTP_X_FORWARDED_FOR")
11
+ or request.META.get("REMOTE_ADDR")
12
+ or "0.0.0.0"
13
+ )
14
+ ip = ip.split(",")[0].strip()
15
+
16
+ # --- User Agent ---
17
+ user_agent_str = request.META.get("HTTP_USER_AGENT", "Desconocido")
18
+ user_agent = parse(user_agent_str)
19
+ navegador = f"{user_agent.browser.family} {user_agent.browser.version_string}"
20
+ sistema = f"{user_agent.os.family} {user_agent.os.version_string}"
21
+
22
+ # --- Geolocalización (usando ipinfo.io gratuita) ---
23
+ geo_data = {}
24
+ try:
25
+ r = requests.get(f"https://ipinfo.io/{ip}/json", timeout=2)
26
+ if r.status_code == 200:
27
+ geo_data = r.json()
28
+ except Exception:
29
+ pass
30
+
31
+ pais = geo_data.get("country", "Desconocido")
32
+ ciudad = geo_data.get("city", "Desconocida")
33
+ isp = geo_data.get("org", "Desconocido")
34
+
35
+ # --- Usuario autenticado ---
36
+ usuario = "Anónimo"
37
+ if request.user and request.user.is_authenticated:
38
+ usuario = request.user.username
39
+
40
+ # --- Construir estructura ---
41
+ datos = {
42
+ "fecha": now().strftime("%Y-%m-%d %H:%M:%S"),
43
+ "ip": ip,
44
+ "pais": pais,
45
+ "ciudad": ciudad,
46
+ "isp": isp,
47
+ "usuario": usuario,
48
+ "user_agent": user_agent_str,
49
+ "navegador": navegador,
50
+ "sistema_operativo": sistema,
51
+ "url": request.path,
52
+ "metodo": request.method,
53
+ }
54
+
55
+ return datos
56
+ except Exception as e:
57
+ return {"error": str(e)}
@@ -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.45
3
+ Version: 0.1.47
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
@@ -10,6 +10,7 @@ GuardianUnivalle_Benito_Yucra.egg-info/dependency_links.txt
10
10
  GuardianUnivalle_Benito_Yucra.egg-info/requires.txt
11
11
  GuardianUnivalle_Benito_Yucra.egg-info/top_level.txt
12
12
  GuardianUnivalle_Benito_Yucra/auditoria/registro_auditoria.py
13
+ GuardianUnivalle_Benito_Yucra/auditoria/utils_auditoria.py
13
14
  GuardianUnivalle_Benito_Yucra/criptografia/cifrado_aead.py
14
15
  GuardianUnivalle_Benito_Yucra/criptografia/intercambio_claves.py
15
16
  GuardianUnivalle_Benito_Yucra/criptografia/kdf.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GuardianUnivalle-Benito-Yucra
3
- Version: 0.1.45
3
+ Version: 0.1.47
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.45"
7
+ version = "0.1.47"
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" }
@@ -1,40 +0,0 @@
1
- import os
2
- import datetime
3
- import json
4
-
5
- LOG_FILE = "auditoria_guardian.log"
6
-
7
-
8
- def registrar_evento(
9
- tipo: str,
10
- descripcion: str = "",
11
- severidad: str = "MEDIA",
12
- extra: dict | None = None,
13
- ):
14
- try:
15
- evento = {
16
- "fecha": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
17
- "tipo": tipo,
18
- "descripcion": descripcion,
19
- "severidad": severidad,
20
- "extra": extra or {},
21
- }
22
-
23
- # ✅ Crear carpeta solo si hay directorio en la ruta
24
- log_dir = os.path.dirname(LOG_FILE)
25
- if log_dir:
26
- os.makedirs(log_dir, exist_ok=True)
27
-
28
- with open(LOG_FILE, "a", encoding="utf-8") as f:
29
- f.write(json.dumps(evento, ensure_ascii=False) + "\n")
30
-
31
- except Exception as e:
32
- print(f"[Auditoría] Error al registrar evento: {e}")
33
-
34
-
35
- def generar_reporte() -> str:
36
- """Devuelve todo el contenido del archivo de auditoría."""
37
- if not os.path.exists(LOG_FILE):
38
- return "No hay registros aún."
39
- with open(LOG_FILE, "r", encoding="utf-8") as f:
40
- return f.read()