El parámetro user_id recibido de la petición HTTP se interpola directamente en la query SQL usando un f-string. Un atacante puede inyectar SQL arbitrario para leer, modificar o eliminar cualquier dato de la base de datos.
Exposición total de la base de datos. Datos PII de todos los clientes accesibles. Posible borrado masivo con '; DROP TABLE orders; --.
Se usa subprocess.run(shell=True) con una cadena construida a partir de input del usuario (nombre de archivo de exportación). Permite ejecutar comandos arbitrarios en el servidor.
RCE (Remote Code Execution). Un atacante con acceso al endpoint puede ejecutar comandos con los permisos del proceso servidor.
El nombre de archivo proporcionado por el usuario se usa sin validación para construir rutas del sistema de ficheros. Un atacante puede usar ../../etc/passwd para leer o sobreescribir archivos fuera del directorio permitido.
Usar [] como valor por defecto en un parámetro de función crea un único objeto compartido entre todas las llamadas. Las columnas añadidas en una llamada persistirán en llamadas futuras, causando bugs difíciles de reproducir en producción.
El método get_metrics() declara retornar dict[str, Any] pero Redis devuelve Optional[bytes]. El código nunca deserializa el valor de Redis, retornando bytes crudos al caller que espera un dict. Causará AttributeError en runtime.
El endpoint de informe itera sobre cada store_id y lanza una query separada a la base de datos. Con 50 tiendas esto genera 50 queries consecutivas. A escala (clientes con cientos de tiendas) esto colapsa la base de datos.
Un bloque except Exception: pass oculta cualquier error — incluyendo errores de I/O, permisos, encoding y desbordamientos de disco. El usuario recibirá un CSV vacío sin ningún mensaje de error.
La función generate_report() tiene 78 líneas, 7 parámetros y no tiene type hints en ninguno de sus argumentos ni en el retorno. Viola el límite de 50 líneas y 5 parámetros. Refactorizar usando un dataclass ReportConfig.
Los f-strings en logging se evalúan siempre, incluso si el nivel de log está desactivado. Usar %s deferrido evita la concatenación innecesaria de strings en producción.
3 ocurrencias de value == None. En Python, None es un singleton; la comparación correcta y eficiente es value is None. El operador == puede ser sobreescrito por __eq__ en objetos personalizados.
TTL hardcodeados como 3600 y 86400 sin explicación. Si el negocio decide cambiar la caché de métricas, no es obvio qué valores cambiar ni por qué.
Ninguna de las 5 funciones públicas (export_csv, export_excel, export_pdf, sanitize_filename, compress_export) tiene docstring. En un módulo que maneja PII, el contrato de cada función debe estar documentado.
Los imports de stdlib, terceros y propios están mezclados sin separación. Añadir isort al pipeline de CI o configurar ruff --select I.
2 print() statements usados para debug que llegaron al PR. En producción no hay forma de desactivarlos ni de capturarlos en el sistema de logging centralizado.
db/queries.py:22 — usar queries parametrizadasdb/queries.py:89 — eliminar shell=Trueutils/file_export.py:45 — validar y sanitizar rutasmetrics_cache.py:45 (crash garantizado en runtime)generate_report() — demasiado larga y sin tipado