letxinet / ANALISIS_BUGS_REFACTORIZACION.md
C2MV's picture
Initial upload for Build Small Hackathon
68fb5e2 verified
|
Raw
History Blame Contribute Delete
17.1 kB

A newer version of the Gradio SDK is available: 6.19.0

Upgrade

Analisis de bugs y refactorizacion

Fecha de revision: 2026-06-08

Resumen ejecutivo

El proyecto compila y la app importa correctamente, pero hay fallos funcionales y de seguridad que pueden hacer que la interfaz prometa capacidades que el backend no ejecuta, que algunas busquedas fallen silenciosamente, y que datos externos se rendericen como HTML sin escape.

Hallazgos mas importantes:

  • La configuracion de fuentes esta desalineada: la UI usa IDs en mayusculas y el motor usa IDs en minusculas.
  • Scopus, CORE y SerpAPI no reciben API keys aunque existan en .env.
  • La normalizacion de anos puede romper busquedas con ValueError o TypeError.
  • Hay varias rutas de XSS/HTML injection al insertar metadatos externos en gr.HTML.
  • Los controles de sistema pueden matar todos los procesos python.exe.
  • La app crea y muestra credenciales por defecto admin/admin123.
  • Las API keys se devuelven al frontend en la pestana de modelos.
  • El mecanismo de detener pipeline usa StopAsyncIteration de forma insegura para async generators.
  • La refactorizacion prioritaria debe centralizar fuentes/providers, sanitizar salidas HTML y separar configuracion sensible del cliente.

Verificaciones realizadas

  • python -m compileall -q .: OK.
  • venv\Scripts\python.exe -c "import app; print('app import ok')": OK.
  • python -m pytest -q: falla porque pytest no esta instalado.
  • venv\Scripts\python.exe -m pytest -q: falla porque pytest no esta instalado en el venv.
  • Se reprodujeron fallos con providers simulados:
    • filtro de fuentes con OPENALEX devuelve 0 resultados, con openalex devuelve 1.
    • year='s.f.' rompe el filtro de anos.
    • mezcla year='2020' y year=2019 rompe el ordenamiento.

Severidad

  • P0: riesgo de seguridad o accion destructiva.
  • P1: rompe flujo principal o fuente importante.
  • P2: inconsistencia funcional, deuda tecnica con alto costo futuro.
  • P3: mejora de robustez, UX o mantenibilidad.

Hallazgos detallados

1. Configuracion de fuentes inconsistente

Severidad: P1

Archivos:

  • modules/config/sources_config_tab.py
  • backend/tools/search_engine.py
  • backend/providers/sources.py
  • config.py
  • modules/research_tab.py

Sintoma:

La pestana de configuracion guarda fuentes como OPENALEX, SCOPUS, LA_REFERENCIA, pero el motor de busqueda filtra contra openalex, scopus, lareferencia.

Causa raiz:

Hay varias fuentes de verdad:

  • modules/config/sources_config_tab.py define IDs en mayusculas.
  • backend/providers/sources.py define grupos reales usados por search_engine.
  • config.py define otro mapa mas amplio con fuentes que no estan implementadas.
  • modules/research_tab.py define ALL_SOURCES manualmente.

Ademas, en sources_config_tab.py, la linea que inicializa _enabled_sources dentro de create_sources_config_tab() no declara global, por lo que crea una variable local. Al arrancar, la configuracion queda ignorada. Cuando el usuario cambia un checkbox, _update_enabled() si usa global, pero guarda IDs en mayusculas y puede filtrar todo.

Impacto:

  • La UI puede decir que una fuente esta activa, pero el backend no la usa.
  • El usuario puede desactivar/activar fuentes y dejar la busqueda sin providers efectivos.
  • Dificulta diagnosticar por que "no hay resultados".

Correccion recomendada:

  • Crear un registro unico de fuentes, por ejemplo backend/providers/registry.py.
  • Usar siempre IDs canonicos en minusculas.
  • Hacer que la UI derive sus opciones desde ese registro.
  • Normalizar aliases antes de guardar configuracion.
  • Eliminar o consolidar config.py si no es la fuente real.

Criterio de aceptacion:

  • Activar OpenAlex en UI guarda openalex.
  • Desactivar openalex realmente evita llamadas a OpenAlex.
  • Seleccionar all expande solo providers implementados o marca claramente los no implementados.

Pruebas minimas:

  • expand_sources(["all"]) solo devuelve IDs canonicos.
  • enabled_sources=["OPENALEX"] se normaliza a ["openalex"].
  • enabled_sources=["openalex"] permite resultados de OpenAlex.

2. Scopus, CORE y SerpAPI no reciben API keys

Severidad: P1

Archivos:

  • backend/tools/search_engine.py
  • backend/providers/scopus.py
  • backend/providers/core_.py
  • backend/providers/serpapi.py

Sintoma:

Los providers premium o con clave devuelven lista vacia si api_key no se pasa como argumento. El motor los llama asi:

PROVIDERS[src](query, limit=min(max_results, 50))

Causa raiz:

Las claves estan en .env, pero no existe una capa que lea SCOPUS_API_KEY, CORE_API_KEY o SERPAPI_API_KEY y las inyecte al provider correspondiente.

Impacto:

  • Scopus, CORE y SerpAPI aparecen configurables pero no funcionan.
  • El sistema no distingue "sin resultados" de "no habia credencial".

Correccion recomendada:

  • En el registro unico, cada provider debe declarar env_key, requires_key y callable.
  • search_engine.search() debe resolver la clave por provider y pasarla como api_key.
  • Si falta una clave requerida, devolver metadata tipo sourceStatus en lugar de silencio.

Criterio de aceptacion:

  • Con SCOPUS_API_KEY en .env, search(..., sources=["scopus"]) llama a Scopus con la clave.
  • Sin clave, el resultado indica scopus: missing_api_key.

3. Filtro y ordenamiento por ano rompen con datos heterogeneos

Severidad: P1

Archivo:

  • backend/tools/search_engine.py

Sintoma:

El filtro usa int(r.get("year", 0)) sin validar. Si un provider devuelve s.f., N/A, una fecha completa o un string no numerico, la busqueda completa falla.

Reproduccion:

  • year='s.f.' + year_start='2020' lanza ValueError.
  • year='2020' y year=2019 sin filtro lanza TypeError al ordenar.

Causa raiz:

No hay normalizacion de metadatos a la entrada del motor.

Correccion recomendada:

  • Crear parse_year(value) -> Optional[int].
  • Normalizar todos los resultados inmediatamente despues de recibirlos.
  • Ordenar con una key que siempre devuelva int: parse_year(x.get("year")) or 0.
  • Los filtros deben ignorar o conservar documentos sin ano segun politica explicita.

Criterio de aceptacion:

  • year="2020", year=2020, year="2020-05-01" se tratan como 2020.
  • year="s.f.", None, "N/A" no rompen.

4. HTML injection / XSS en resultados, referencias y grafo

Severidad: P0

Archivos:

  • modules/search_tab.py
  • modules/research_tab.py
  • modules/graph_module.py
  • assets/custom.js

Sintoma:

Campos de proveedores externos se insertan directo en HTML:

  • titulo
  • autores
  • abstract
  • DOI
  • PDF URL
  • fuente
  • contenido generado por IA

Tambien se usan onclick inline y innerHTML.

Impacto:

Un resultado academico malicioso o un dato corrupto puede inyectar HTML/JS en la app. Aunque sea local, el riesgo aumenta si se comparte con usuarios, se usa share=True, o se abre en red.

Correccion recomendada:

  • Escapar texto con html.escape.
  • Validar URLs con urllib.parse; permitir solo http y https.
  • Construir atributos JS con json.dumps, no con reemplazos manuales.
  • Evitar onclick inline; delegar eventos desde JS con data-*.
  • En grafo, usar textContent para texto y crear nodos DOM en vez de concatenar strings con innerHTML.
  • Revisar salida Markdown a HTML; si se usa markdown, sanitizar o restringir tags.

Criterio de aceptacion:

  • Un titulo como <img src=x onerror=alert(1)> se muestra como texto, no ejecuta codigo.
  • Una URL javascript:alert(1) no se renderiza como link.

5. Controles destructivos: reiniciar y matar procesos

Severidad: P0

Archivo:

  • app.py

Sintoma:

Los botones de control ejecutan:

  • taskkill /F /IM python.exe /T
  • taskkill con shell=True

Impacto:

  • Puede matar la app actual.
  • Puede matar otros procesos Python del usuario.
  • Puede interrumpir trabajos no relacionados.

Correccion recomendada:

  • Eliminar el boton "Matar Procesos" de la UI normal.
  • Si se requiere restart, reiniciar solo el proceso actual con un supervisor controlado.
  • Evitar shell=True.
  • No matar por nombre de proceso global.

Criterio de aceptacion:

  • Ningun boton de UI mata todos los python.exe.
  • Reinicio, si existe, afecta solo a la instancia actual.

6. Credenciales por defecto y hash debil

Severidad: P0/P1 segun despliegue

Archivo:

  • app.py

Sintoma:

La app crea admin/admin123 y lo muestra en el mensaje de login. La contrasena se almacena con SHA-256 simple, sin sal ni factor de coste.

Impacto:

  • Cualquier usuario que vea el login sabe la credencial.
  • Si se filtra la base, el hash es barato de romper.

Correccion recomendada:

  • Exigir LETXIPU_ADMIN_PASSWORD o crear usuario en un comando setup.
  • No mostrar credenciales en UI.
  • Usar passlib con bcrypt/argon2 o werkzeug.security.
  • Forzar cambio de password inicial.

Criterio de aceptacion:

  • Sin password configurado, la app no crea admin debil.
  • El mensaje de login no contiene credenciales.

7. API keys expuestas al frontend

Severidad: P0/P1

Archivo:

  • modules/config/ai_tab.py

Sintoma:

La pestana de modelos precarga MISTRAL_API_KEY en un textbox y al cambiar provider devuelve la clave al cliente.

Impacto:

  • Cualquier persona autenticada puede inspeccionar el HTML/estado y extraer secretos.
  • Aumenta el riesgo si la app se comparte.

Correccion recomendada:

  • Mostrar solo estado: configurada/no configurada.
  • Si se permite actualizar claves, hacerlo con un flujo de escritura al .env cuidadosamente autorizado.
  • Nunca devolver claves existentes al navegador.

Criterio de aceptacion:

  • El frontend no recibe valores completos de API keys.
  • El endpoint/evento solo devuelve mascara, por ejemplo sk-...abcd.

8. Detener pipeline puede terminar como error generico

Severidad: P1/P2

Archivos:

  • backend/pipeline.py
  • modules/research_tab.py

Sintoma:

_checkpoint() levanta StopAsyncIteration. En async generators, Python convierte esto en RuntimeError: async generator raised StopAsyncIteration.

Impacto:

  • El boton detener puede mostrar error generico en vez de estado "detenido".
  • La limpieza final puede no ejecutarse como se esperaba.

Correccion recomendada:

  • Crear excepcion propia:
class PipelineStopped(Exception):
    pass
  • Levantar PipelineStopped.
  • Capturar PipelineStopped en handlers.

Criterio de aceptacion:

  • Pulsar detener muestra estado detenido y no un traceback/error generico.

9. Cambio de proveedor IA en Research devuelve forma incorrecta

Severidad: P2

Archivo:

  • modules/research_tab.py

Sintoma:

update_models() devuelve un solo gr.update, pero esta conectado a tres outputs: busqueda, sintesis y traduccion.

Impacto:

  • Al cambiar proveedor, Gradio puede fallar o actualizar solo un componente.

Correccion recomendada:

  • Devolver tres updates:
update = gr.update(choices=models, value=models[0])
return update, update, update

o una lista de tres updates.

Criterio de aceptacion:

  • Cambiar de mistral a groq actualiza los tres dropdowns.

10. Descarga de PDFs: SSRF, TLS deshabilitado y sin limite de tamano

Severidad: P0/P1

Archivos:

  • backend/tools/pdf_tools.py
  • backend/tools/pdf_processor.py
  • modules/pdf_tab.py
  • modules/chat_tab.py

Sintoma:

Se descargan URLs ingresadas por el usuario desde el servidor. Algunas descargas usan verify=False. No hay limite de tamano ni allowlist/bloqueo de IPs internas.

Impacto:

  • SSRF contra servicios internos si la app se expone.
  • Descarga de archivos enormes que agotan memoria o disco.
  • TLS sin verificar permite MITM.

Correccion recomendada:

  • Activar verificacion TLS.
  • Bloquear IPs privadas/locales: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local, metadata cloud.
  • Limitar tamano por Content-Length y streaming con max bytes.
  • Permitir solo http/https.
  • Reusar un downloader comun.

Criterio de aceptacion:

  • URL http://127.0.0.1/... se rechaza.
  • PDF mayor al limite se corta con error claro.

11. Catalogo de fuentes promete providers no implementados

Severidad: P2

Archivos:

  • README.md
  • config.py
  • modules/config/sources_config_tab.py
  • backend/providers/sources.py
  • backend/tools/search_engine.py

Sintoma:

Se mencionan fuentes como SciELO, CONAHCyT, UNAM, ANID, OasisBR, SNRD, MinCiencias, OpenReview, PapersWithCode o HuggingFace, pero el mapa real PROVIDERS no contiene implementaciones para muchas de ellas.

Impacto:

  • La UI genera expectativas falsas.
  • Los grupos all, latam, ai_ml pueden incluir fuentes que no hacen nada.

Correccion recomendada:

  • El registro unico debe marcar implemented=True/False.
  • La UI debe ocultar fuentes no implementadas o mostrarlas como "proximamente".
  • Los grupos operativos deben incluir solo implementadas.

Criterio de aceptacion:

  • No se puede seleccionar una fuente no implementada como si estuviera activa.

12. Manejo de errores silencioso en providers

Severidad: P2

Archivos:

  • backend/providers/*.py
  • backend/providers/base.py
  • backend/tools/search_engine.py

Sintoma:

Muchos providers hacen except Exception: return []. fetch_json() tambien convierte cualquier error en {"error": str(e)}.

Impacto:

  • Timeouts, credenciales faltantes, 403/429 y errores de parseo se ven igual que "0 resultados".
  • Dificulta depurar fuentes rotas.

Correccion recomendada:

  • Devolver estructura por fuente:
{
  "source": "openalex",
  "ok": true,
  "results": [],
  "error": None,
  "status": "ok"
}
  • search() debe conservar sourceErrors y mostrarlos en UI.

Criterio de aceptacion:

  • Si PubMed falla por timeout, la UI muestra "PubMed timeout" y no solo "sin resultados".

Plan de refactorizacion recomendado

Fase 1: estabilizacion funcional

Objetivo: que las busquedas basicas sean confiables.

Tareas:

  • Crear backend/providers/registry.py.
  • Consolidar grupos y aliases en un solo lugar.
  • Normalizar IDs de fuentes a minusculas.
  • Pasar API keys por provider desde .env.
  • Agregar parse_year() y normalizacion de resultados.
  • Corregir update_models() para multiples outputs.
  • Reemplazar StopAsyncIteration por PipelineStopped.

Resultado esperado:

  • search() no se cae por anos raros.
  • Fuentes activadas en UI coinciden con providers usados.
  • Scopus/CORE/SerpAPI usan claves si existen.
  • Detener pipeline funciona sin error generico.

Fase 2: seguridad de interfaz y secretos

Objetivo: eliminar riesgos P0.

Tareas:

  • Escapar HTML en search_tab, research_tab y graph_module.
  • Validar links antes de renderizar.
  • Quitar onclick inline donde sea posible.
  • No devolver API keys al frontend.
  • Remover credenciales por defecto o exigir password por env.
  • Eliminar controles taskkill globales.

Resultado esperado:

  • Datos externos no ejecutan HTML/JS.
  • Las claves no viajan al navegador.
  • La app no mata procesos ajenos.

Fase 3: downloader PDF seguro

Objetivo: robustecer lectura/vectorizacion/chat con PDFs.

Tareas:

  • Crear backend/tools/downloader.py.
  • Bloquear IPs internas y esquemas no permitidos.
  • Descargar en streaming con limite de tamano.
  • Activar TLS verification.
  • Unificar pdf_tools.py, pdf_processor.py y chat_tab.py.

Resultado esperado:

  • El PDF local funciona sin abrir SSRF ni OOM.

Fase 4: arquitectura de resultados y errores

Objetivo: dejar de mezclar "sin resultados" con "fuente rota".

Tareas:

  • Definir SearchResult y SourceSearchOutcome.
  • Hacer que providers devuelvan resultados normalizados.
  • Agregar sourcesUsed, sourcesSkipped, sourceErrors.
  • Mostrar estado por fuente en UI.

Resultado esperado:

  • La UI puede decir: "OpenAlex OK, PubMed timeout, Scopus falta API key".

Fase 5: pruebas y CI local

Objetivo: evitar regresiones.

Tareas:

  • Agregar pytest a requirements-dev.txt o requirements.txt.
  • Tests unitarios para:
    • expansion de fuentes
    • normalizacion de aliases
    • parseo de anos
    • providers con/sin API key
    • escape HTML
    • detener pipeline
  • Tests de integracion con providers simulados.

Resultado esperado:

  • pytest -q corre sin depender de red.
  • Los bugs reproducidos quedan cubiertos.

Orden de implementacion sugerido

  1. registry.py de fuentes/providers.
  2. Normalizacion de resultados y anos.
  3. API keys por provider.
  4. Correccion de update_models() y PipelineStopped.
  5. Escape HTML y validacion de URLs.
  6. Retiro de taskkill, credenciales por defecto y exposicion de secrets.
  7. Downloader PDF seguro.
  8. Tests.

Archivos mas importantes para tocar

  • backend/tools/search_engine.py
  • backend/providers/sources.py
  • backend/providers/registry.py nuevo
  • modules/config/sources_config_tab.py
  • modules/research_tab.py
  • modules/search_tab.py
  • modules/graph_module.py
  • modules/config/ai_tab.py
  • backend/tools/pdf_tools.py
  • backend/tools/pdf_processor.py
  • modules/chat_tab.py
  • app.py
  • requirements.txt o requirements-dev.txt

Nota sobre el estado del repo

El arbol de trabajo ya tenia cambios modificados y archivos sin seguimiento antes de este informe. No se debe hacer reset ni revertir esos cambios sin revisar su origen.