Bandit recorre el árbol sintáctico de Python y señala patrones asociados a riesgos, como subprocess inseguro, hashes débiles y posibles secretos. Un hallazgo inicia una investigación, no demuestra automáticamente una vulnerabilidad.

Esta clase de herramienta se conoce como análisis estático de seguridad, o SAST. Al trabajar sobre el árbol sintáctico, Bandit reconoce construcciones del lenguaje con más precisión que una búsqueda textual. Sin embargo, no conoce todos los límites de confianza, controles del despliegue ni el origen real de cada valor. Una persona debe relacionar el aviso con el flujo de datos de la aplicación.

Ejecutar el análisis

python -m pip install "bandit[toml]"
bandit -r src
bandit -r src --severity-level high

Revisa el resultado completo antes de elegir el límite de CI. No ocultes alertas medias relevantes configurando solo severidad alta.

Un aviso sobre subprocess puede ser aceptable con argumentos constantes y shell=False, pero crítico si recibe entrada del usuario. Sigue el dato desde su origen. La guía de seguridad de APIs explica estas fronteras.

Cada resultado incluye identificador de prueba, ubicación, severidad y confianza. La severidad estima el impacto potencial; la confianza expresa con qué seguridad el plugin reconoció el patrón. Ninguno de esos valores mide la probabilidad de explotación en tu entorno. Un hallazgo medio en un endpoint público puede requerir atención antes que uno alto en un script de mantenimiento aislado.

Elige un formato legible por máquinas cuando otro sistema consuma el resultado:

bandit -r src -f json -o bandit-report.json
bandit -r src -f sarif -o bandit-report.sarif

Trata esos informes como artefactos internos. Pueden revelar rutas, fragmentos del código y detalles de implementación. Aplica control de acceso y una retención razonable en vez de publicarlos desde una tarea de CI abierta.

Configurar el proyecto en pyproject.toml

El extra toml permite que Bandit lea opciones de pyproject.toml. Una configuración versionada mantiene el mismo comportamiento en local y en CI:

[tool.bandit]
exclude_dirs = ["tests/fixtures", ".venv"]
skips = ["B101"]

Cada exclusión necesita una razón revisable. Omitir B101, relacionado con assert, solo tiene sentido si el equipo confirmó que el código analizado no depende de aserciones para validar seguridad y el alcance es correcto. Excluir todo el árbol de pruebas también puede ocultar utilidades ejecutadas en pipelines. Es preferible excluir fixtures deliberadamente inseguros que un directorio completo.

Las opciones -t y -s seleccionan u omiten pruebas. Son prácticas para una investigación, pero una política permanente debe quedar en el repositorio. Así, cualquier reducción de cobertura aparece en la revisión del código.

Interpretar y corregir hallazgos frecuentes

La clasificación debe determinar si existe una ruta controlada por un atacante y eliminar la causa. Algunos grupos aparecen a menudo:

  • comandos y procesos: pasa una lista de argumentos a subprocess.run, conserva shell=False y usa una lista permitida cuando la entrada cambia la operación;
  • archivos temporales: utiliza APIs que creen nombres imprevisibles y permisos adecuados, no rutas predecibles construidas manualmente;
  • criptografía y hashes: no uses MD5 o SHA-1 para contraseñas o decisiones de seguridad; elige una biblioteca mantenida y un algoritmo apropiado;
  • deserialización: considera los datos de pickle como código ejecutable y nunca los cargues desde una fuente no confiable;
  • TLS: no desactives la validación del certificado para arreglar un entorno; corrige su cadena de confianza;
  • posibles secretos: retira credenciales del código y rótalas si un valor real llegó al historial.

Este comando es peligroso:

import subprocess


def consultar(host: str) -> None:
    subprocess.run(f"ping -c 1 {host}", shell=True, check=True)

Una expresión regular podría rechazar algunas cadenas maliciosas, pero el shell seguiría interpretando datos. Separar los argumentos elimina esa interpretación:

import ipaddress
import subprocess


def consultar(host: str) -> None:
    direccion = str(ipaddress.ip_address(host))
    subprocess.run(["ping", "-c", "1", direccion], check=True)

La función corregida también hace explícita la política: acepta una dirección IP, no un fragmento arbitrario de comando. Según el entorno todavía harán falta límites de tiempo, privilegios mínimos y controles de red.

Tratar falsos positivos con precisión

Antes de silenciar un resultado, reproduce la ruta, identifica quién controla el valor y documenta por qué el control existente basta. Si la excepción está justificada, coloca # nosec en la línea e indica la prueba, por ejemplo # nosec B603. Una explicación cercana aporta evidencia a futuras revisiones. Un # nosec genérico podría ocultar otro plugin añadido después.

Revisa las exclusiones periódicamente. Las funciones internas pasan a ser públicas, la validación desaparece durante un refactor y las suposiciones caducan. Buscar nosec en un pull request ayuda a ver si aumentan, aunque no reemplaza el análisis del contexto.

Una baseline permite registrar hallazgos existentes para introducir Bandit sin bloquear todo el desarrollo. Versiona el archivo, bloquea resultados nuevos, asigna responsables y planifica la reducción. La baseline no debe convertirse en un permiso permanente para riesgos antiguos. Si cambia el código cercano a uno de sus registros, vuelve a evaluarlo.

Ejecutar Bandit como control de CI

Una tarea mínima instala una versión controlada y analiza solo el código de la aplicación:

python -m pip install "bandit[toml]==1.8.6"
bandit -c pyproject.toml -r src

Fijar la versión hace reproducibles los resultados. También debes actualizarla de forma planificada porque las versiones incorporan comprobaciones y correcciones. Hazlo en un pull request específico, revisa los cambios y clasifica los avisos nuevos. Mantener una versión obsoleta solo para conservar la tarea verde debilita el control.

Distingue un fallo de la herramienta de un análisis limpio. Una instalación fallida, una configuración inválida o una ruta equivocada deben fallar de forma visible. Conserva informes solo con acceso restringido y deja suficiente salida para que el equipo ubique el problema.

Bandit es una capa más. Los linters y tests detectan defectos generales, la auditoría de dependencias encuentra paquetes vulnerables, el análisis de secretos localiza credenciales y las pruebas dinámicas ejercitan el sistema en ejecución. El modelado de amenazas y la revisión humana relacionan esas señales con los activos y fronteras importantes.

Registra el resultado de la clasificación en lugar de convertir el número bruto de hallazgos en un objetivo. Son datos útiles la regla, el componente, la decisión, la persona responsable y la fecha de revisión. Una reducción puede significar menos riesgo, pero también exclusiones más amplias. Revisa tanto la configuración como las correcciones. Si un error se repite, crea un helper seguro y una prueba específica para ofrecer un camino sencillo. La mejora más eficaz suele ser hacer innecesaria la construcción insegura, no pedir que cada revisor vuelva a detectarla.

Ejecuta Bandit antes del merge y en CI. Una baseline permite adopción gradual si bloqueas hallazgos nuevos y asignas responsable a la deuda. Evita exclusiones amplias y documenta cada excepción.

Bandit no audita versiones de dependencias. Combínalo con pip-audit, pruebas de autorización y revisión de arquitectura.

La documentación oficial de Bandit y su referencia de configuración, consultadas el 22 de julio de 2026, explican plugins, severidad, confianza, formatos y baselines. El objetivo es corregir causas, no fabricar cero alertas.