From e1e538a3e2a01cdd2ada28db233e25a1adbf2186 Mon Sep 17 00:00:00 2001 From: Felipe Luis Quezada Valenzuela Date: Thu, 9 Jan 2025 10:17:54 -0300 Subject: [PATCH] #edit config --- config.py | 3 ++ defs.py | 130 +++++++++++++++++++++++++++++++++++++++++++++------ run_daily.py | 26 ++++++----- 3 files changed, 133 insertions(+), 26 deletions(-) diff --git a/config.py b/config.py index f199a37..9f81666 100644 --- a/config.py +++ b/config.py @@ -36,3 +36,6 @@ IOC_TIPOS_OMITIR = [ WORKERS_THR = 4 +# Organizaciones que no se guardaran en BD (No se refleja conteo de IoC) +ORG_OMITIR = [] + diff --git a/defs.py b/defs.py index 338d3c1..3e95f9d 100644 --- a/defs.py +++ b/defs.py @@ -8,13 +8,14 @@ import ipaddress import json from concurrent.futures import ThreadPoolExecutor, as_completed from sqlalchemy import create_engine +from sqlalchemy.exc import IntegrityError, SQLAlchemyError from sqlalchemy.orm import sessionmaker from pymisp import PyMISP from pymisp.exceptions import PyMISPError import config import urllib3 import requests -from models import Base, Registro, Usuario +from models import Base, Registro, Usuario, ModificadosEv from datetime import datetime, timedelta import threading @@ -288,6 +289,10 @@ class MISPProcessorTop: if int(evento['Event']['attribute_count']) > 0: self.misp.publish(int(e['Event']['id'])) logging.info("Publicando cambios de evento #" + e['Event']['id']) + + # Se guarda uuid de evento que fue modificado en BD, manteniendo el timestamp de publish + self.guardar_bd_procesados_mod(e['Event']['uuid'], int(e['Event']['publish_timestamp']), datetime.now()) + else: # Se elimina evento con cero atributo... self.misp.delete_event(int(e['Event']['id'])) @@ -295,6 +300,20 @@ class MISPProcessorTop: except (Exception, PyMISPError) as err: logging.error(str(err)) return resultados + + @staticmethod + def convert_value(value): + try: + # Intentar convertir a fecha y retornar un número representativo + return int(datetime.strptime(value, "%Y%m%d").strftime("%Y%m%d")) + except ValueError: + # Si falla, convertir a entero directamente + return int(value) + + def find_max(self, data): + # Llamar al método estático directamente + max_value = max(data, key=self.convert_value) + return max_value def calcula_calidad_iocs(self, desde: str, hasta: str, a_por_evento=None): try: @@ -306,10 +325,6 @@ class MISPProcessorTop: puntos = {k: 0 for k in self.creators_accounts.keys()} - # Para filtrar versión de Warninglist donde buscar, se toma desde fecha "desde" - actual = datetime.now().year - version = str(actual) - # Fechas para sacar la mayor... fechas = [] @@ -328,12 +343,11 @@ class MISPProcessorTop: wl = self.misp.warninglists() for l in wl: - if str(l['Warninglist']['version']).startswith(str(actual)): - fechas.append(l['Warninglist']['version']) + fechas.append(str(l['Warninglist']['version'])) - if fechas: - # Saca la versión más alta... - version = max(fechas) + # Saca la versión más alta... + #version = max(fechas, key=self.convert_value) + version = self.find_max(fechas) for l in wl: if str(l['Warninglist']['version']) == version: @@ -342,11 +356,11 @@ class MISPProcessorTop: lista.append(self.misp.get_warninglist(int(l['Warninglist']['id']))) if lista: - # Promedio por defecto prom = 0 logging.info("Warninglist de MISP Cargadas : " + str(len(lista))) + logging.info("Versión de Warninglists a utilizar: " + str(version)) # Rango completo de fechas.... logging.info("Buscando IoC Desde :" + desde + " Hasta :" + hasta) @@ -364,10 +378,22 @@ class MISPProcessorTop: # Se seleccionan eventos para establecer limite de fechas for e in eventos_tmp: if datetime.fromtimestamp(int(e['publish_timestamp'])).date() <= datetime.strptime(hasta, '%Y-%m-%d').date(): - - # Event get - ev = self.misp.get_event(int(e['id'])) - eventos.append(ev) + try: + # se verifica que evento no haya sido procesado en periodo anterior + prev_evento = self.obtener_evento_mod_db(e['uuid']) + + # Si no existe en modificados.... + if not prev_evento: + # Event get + ev = self.misp.get_event(int(e['id'])) + eventos.append(ev) + elif datetime.fromtimestamp(int(e['publish_timestamp'])) > prev_evento['pub_mod_fecha'] + timedelta(hours=1): + # Verificar fecha con desfase, si es mayor se agrega para procesar + # Event get + ev = self.misp.get_event(int(e['id'])) + eventos.append(ev) + except Exception as err_c: + logging.error("Error al tratar de agregar evento +"+e['id']+" :"+str(err_c)) # Atributos por evento es None, se calcula promedio... if a_por_evento is None: @@ -427,6 +453,80 @@ class MISPProcessorTop: logging.info("Data volcada a ruta :" + salida) except Exception as e: logging.info("Error al escribir JSON :" + str(e)) + + + def obtener_evento_mod_db(self, e_uuid: str): + # Output inicial vacío + output = {} + + try: + # Ruta de la base de datos + ruta_base_datos = os.path.join(self.dir_data, "registros.db") + engine = create_engine(f'sqlite:///{ruta_base_datos}') + Base.metadata.create_all(engine) + Session = sessionmaker(bind=engine) + session = Session() + + try: + # Consultar el último registro con el evento_uuid específico + ultimo_procesado = ( + session.query(ModificadosEv) + .filter_by(evento_uuid=e_uuid) + .order_by(ModificadosEv.pub_mod_fecha.desc()) # Ordenar por fecha descendente + .first() # Obtener solo el primer registro + ) + + # Si se encuentra un registro, convertirlo a dict + if ultimo_procesado: + output = ultimo_procesado.to_dict() + + except SQLAlchemyError as db_error: + logging.error(f"Error en la consulta a la base de datos: {db_error}") + session.rollback() + finally: + session.close() + + except Exception as err: + logging.error(f"Error general al conectarse a la base de datos: {err}") + + # Retornar el resultado + return output + + + def guardar_bd_procesados_mod(self, e_uuid: str, timestamp_fecha: int, mod_fecha: datetime): + try: + # Ruta de la base de datos + ruta_base_datos = os.path.join(self.dir_data, "registros.db") + engine = create_engine(f'sqlite:///{ruta_base_datos}') + Base.metadata.create_all(engine) + Session = sessionmaker(bind=engine) + session = Session() + + # Crear el nuevo registro + nuevo_registro = ModificadosEv( + evento_uuid=e_uuid, + publicado_fecha=datetime.fromtimestamp(timestamp_fecha), + pub_mod_fecha=mod_fecha + + ) + + try: + session.add(nuevo_registro) + session.commit() + logging.info(f"Registrado UUID de evento modificado: {e_uuid}") + except IntegrityError: + # Manejo específico para violación de restricciones + logging.warning(f"Registro duplicado: {e_uuid} ya existe con la misma fecha.") + session.rollback() + except Exception as e: + logging.error(f"Error al guardar los datos en la base de datos: {e}") + session.rollback() + finally: + session.close() + + except Exception as err: + logging.error(f"Error general al guardar en la base de datos: {err}") + def guardar_bd(self, data: list, fecha: str): try: diff --git a/run_daily.py b/run_daily.py index d1826f0..421a9c9 100644 --- a/run_daily.py +++ b/run_daily.py @@ -1,5 +1,6 @@ from defs import MISPProcessorTop import calendar +from config import ORG_OMITIR from datetime import datetime, timedelta @@ -25,16 +26,19 @@ obj = MISPProcessorTop() # Se llama a calcular fecha #iocs = obj.calcula_calidad_iocs(desde_fecha, hasta_fecha, 1000) - -iocs = obj.calcula_calidad_iocs(fecha_anterior, fecha_anterior, max_ioc) - -if iocs: - # Solo para efectos de backup fisico por dia... - #obj.guarda_ioc_json(iocs, fecha_anterior.strftime('%Y%m%d')"_"+".json") - - # Se imprime estructura +try: + iocs = obj.calcula_calidad_iocs(fecha_anterior, fecha_anterior, max_ioc) print(iocs) + + if iocs: + # Solo para efectos de backup fisico por dia... + #obj.guarda_ioc_json(iocs, fecha_anterior.strftime('%Y%m%d')"_"+".json") + output = [] + for x in iocs: + if x['comunidad'] not in ORG_OMITIR: + output.append(x) - - # Guarda en BD (SQLite) - obj.guardar_bd(iocs,fecha_anterior) \ No newline at end of file + # Guarda en BD (SQLite) + obj.guardar_bd(output,fecha_anterior) +except Exception as e: + print(str(e)) \ No newline at end of file