Análisis de vulnerabilidades y despliegue automatizado de una app con Bearer CLI y ESLint
Autores: Gabriela Estefania Cohaila Alvarado y Jhony Vargas Luque. En este proyecto construimos Notas Seguras, una aplicación de tareas en JavaScript, para estudiar cómo detectar y corregir problemas de seguridad antes
Autores: Gabriela Estefania Cohaila Alvarado y Jhony Vargas Luque.
En este proyecto construimos Notas Seguras, una aplicación de tareas en JavaScript, para estudiar cómo detectar y corregir problemas de seguridad antes de publicar una nueva versión. Comparamos implementaciones educativas inseguras con el código corregido mediante Bearer CLI y ESLint con reglas de seguridad, y conectamos los controles con un despliegue automatizado en GitHub Pages.
Los resultados fueron concretos: Bearer reportó dos hallazgos altos en los componentes vulnerables; ESLint señaló esos mismos puntos y un uso adicional de eval. Después de corregir el código, ambos analizadores terminaron sin hallazgos en la aplicación publicable y las cinco pruebas de regresión pasaron. En este artículo presentamos conjuntamente los hallazgos, las correcciones y la automatización.
Código, aplicación y video del equipo
- Repositorio público de Notas Seguras.
- Aplicación pública en GitHub Pages.
- Ejecución comprobada del análisis y despliegue.
- Informes de la ejecución de referencia.
La aplicación y el alcance del experimento
Notas Seguras permite crear tareas con título, descripción, prioridad y minutos estimados; también buscar, filtrar, completar y eliminar tareas. Está desarrollada con HTML, CSS y JavaScript. Guarda la información mediante localStorage en el navegador, sin cuentas, sincronización ni backend de negocio. GitHub Pages sirve los archivos estáticos de la aplicación por HTTPS.
El repositorio separa dos alcances. examples/vulnerable/ conserva las implementaciones inseguras de tres funciones para realizar la comparación. app/ contiene la aplicación completa con esas funciones corregidas. La baseline es un ejemplo educativo de componentes, no una aplicación anterior que se haya publicado. El escaneo inicial cubre esos componentes; el análisis final cubre toda la aplicación publicable.
Selección de herramientas distintas a los laboratorios
Revisamos las cuatro guías de laboratorio compartidas: lab01 utiliza SonarQube/SonarCloud; lab02, Snyk; lab03, Semgrep; y lab04, tfsec y SonarCloud. Para este trabajo elegimos Bearer CLI 2.1.1 y ESLint 10.12.0 con eslint-plugin-no-unsanitized 4.1.5, que no aparecen en esas cuatro guías.
Bearer figura en el catálogo de analizadores de código de OWASP. También consultamos el directorio de analizadores de NIST como referencia. La inclusión en un catálogo no certifica la seguridad del proyecto ni implica un aval de sus resultados.
ESLint es un analizador configurable. En nuestro caso, el plugin de Mozilla prohíbe operaciones de inserción HTML inseguras y las reglas de ESLint restringen evaluación dinámica de JavaScript. Lo usamos como control complementario de patrones de seguridad, sin atribuirle una auditoría general de todos los riesgos de la aplicación. Esta configuración no requiere una cuenta de escaneo.
Preparación y ejecución de los análisis
Con Node.js 24 instalado, se puede reproducir el proyecto:
git clone https://github.com/GabrielaCohailaAlvaradoE/notas-seguras-sast.git
cd notas-seguras-sast
npm ci
npm test
npm run scan:eslint
Para ejecutar Bearer, utilizar Linux x86_64 o Ubuntu en WSL. Desde la raíz del mismo repositorio:
bash scripts/install-bearer.sh
bash scripts/scan-bearer.sh
node scripts/summarize.mjs
El instalador descarga Bearer 2.1.1 de su publicación oficial y verifica el archivo con la suma SHA256 publicada para esa versión. El script analiza primero examples/vulnerable/ y después app/, genera informes JSON y produce también SARIF para el código corregido. reports/ contiene los resultados de cada ejecución; evidence/ conserva una instantánea verificable de la ejecución enlazada.
Para abrir la aplicación localmente, ejecutar npm start y visitar http://127.0.0.1:4173.
Hallazgo 1 y 2: inserción de texto como HTML
Las implementaciones vulnerables del título y la descripción usaban este patrón:
export function renderTitle(element, userInput) {
element.innerHTML = userInput;
}
El navegador interpreta el contenido asignado a innerHTML como marcado. Si esa entrada está controlada por un atacante, puede introducir contenido activo y generar riesgo de XSS. En esta aplicación local, la exposición depende de cómo llegue la entrada al navegador; no afirmamos que exista un servidor multiusuario comprometido.
Bearer reportó dos hallazgos altos de la regla javascript_lang_dangerous_insert_html, asociados a CWE-79, en las líneas 4 y 7 de examples/vulnerable/rendering.js. ESLint señaló las mismas asignaciones con no-unsanitized/property.
Las tareas no necesitan contenido HTML enriquecido, por lo que corregimos ambas funciones con textContent:
export function renderTitle(element, userInput) {
element.textContent = userInput;
}
Con este cambio, los datos se presentan como texto. Una entrada como <b>Prueba de texto</b> se muestra literalmente, sin convertirse en marcado. Esta comprobación visual relaciona la corrección con un comportamiento observable.
Hallazgo 3: evaluación dinámica de los minutos
El componente vulnerable para interpretar minutos utilizaba:
export function parseMinutes(userInput) {
return eval(userInput);
}
eval puede interpretar la entrada como JavaScript, aunque el propósito del campo sea registrar una duración. La regla no-eval de ESLint detectó esta operación en la línea 4 de examples/vulnerable/core.js. Bearer no la reportó en este ejemplo; no le atribuimos ese hallazgo.
La corrección elimina la evaluación de expresiones y valida el dominio permitido:
export function parseMinutes(userInput) {
const value = String(userInput).trim();
if (!/^\d{1,4}$/.test(value)) {
throw new Error('Usa minutos enteros entre 1 y 1440.');
}
const minutes = Number(value);
if (minutes < 1 || minutes > 1440) {
throw new Error('Usa minutos enteros entre 1 y 1440.');
}
return minutes;
}
Una entrada como 25 es válida; 20+5, valores fuera de rango o código JavaScript se rechazan. El producto necesita minutos enteros, no una calculadora de expresiones.
Resultados después de corregir
| Punto del código vulnerable | Bearer CLI | ESLint | Corrección |
|---|---|---|---|
Título, rendering.js:4
|
XSS alto, CWE-79 | no-unsanitized/property |
textContent |
Descripción, rendering.js:7
|
XSS alto, CWE-79 | no-unsanitized/property |
textContent |
Minutos, core.js:4
|
No reportado en este caso | no-eval |
Validación y Number
|
| Medición | Componentes vulnerables | Aplicación corregida |
|---|---|---|
| Hallazgos de Bearer | 2 altos | 0 |
| Errores de reglas de ESLint | 3 | 0 |
| Pruebas de regresión aprobadas | No aplica | 5 |
Son tres puntos inseguros, no cinco vulnerabilidades diferentes: los dos analizadores coinciden en las inserciones HTML. Tampoco se debe equiparar un error de regla de ESLint con la escala de severidad de Bearer.
Las pruebas comprueban límites válidos de minutos, rechazo de expresiones y código, uso de texto en ambos renderizadores, manejo de JSON local dañado y filtrado de registros inválidos con un límite de almacenamiento. La app incluye además una política CSP, pero la desaparición de las alertas se debe a corregir las funciones, no a ocultar los hallazgos.
Automatización de los controles y del despliegue
El archivo .github/workflows/security-and-deploy.yml se activa con pushes a main, solicitudes de cambio y ejecuciones manuales. Su trabajo security realiza los siguientes pasos:
- Descarga el código e instala Node.js 24.
- Reproduce las dependencias con
npm ciy ejecuta las cinco pruebas. - Analiza la baseline y la aplicación con ESLint.
- Instala Bearer, verifica SHA256 y analiza ambos alcances.
- Genera el resumen y guarda el artefacto
security-reports. - Construye la app y prepara los archivos para Pages.
El despliegue se ejecuta después y únicamente para main, fuera de las solicitudes de cambio. La dependencia fundamental es:
deploy:
needs: security
permissions:
pages: write
id-token: write
Si falla el trabajo de seguridad, no se publica esa nueva versión. El sitio previamente desplegado puede seguir disponible. El trabajo de análisis tiene permiso de lectura; los permisos de publicación se conceden en el trabajo de despliegue.
La baseline contiene problemas intencionales, así que el script de Bearer espera código de salida 1 en ese análisis. En cambio, el escaneo de app/ debe finalizar con éxito normal. No se fuerza --exit-code 0 ni se aplica continue-on-error al control de la aplicación publicable. El comparador de ESLint exige tres errores en los componentes educativos y cero en la app.
La construcción copia exclusivamente seis archivos de app/; no publica los ejemplos vulnerables, los scripts ni node_modules. Las acciones de GitHub están fijadas por SHA de commit y las dependencias de Node por versión y lockfile. Los informes de Actions se conservan siete días, y la copia versionada en evidence/ conserva la ejecución de referencia.
Publicación en un proveedor SaaS público
Configuramos GitHub Actions como origen de publicación en Settings → Pages. GitHub Pages aloja la aplicación estática mediante HTTPS. El proyecto utiliza un repositorio público y runners estándar ubuntu-latest, sin contratar servicios de pago ni configurar una clave personal de nube en el código.
La ejecución enlazada terminó con security y deploy en estado satisfactorio. El sitio respondió HTTP 200 y verificamos en el navegador la creación de tareas y la presentación literal de etiquetas. Así, la evidencia conecta el código, el análisis, la automatización y la aplicación disponible públicamente.
Conclusiones del equipo
La comparación mostró una diferencia útil de cobertura: Bearer detectó las dos inserciones HTML inseguras y ESLint añadió el uso de eval. Corregir las operaciones y comprobar el comportamiento permitió pasar de 2 a 0 hallazgos en Bearer, de 3 a 0 errores en ESLint y obtener cinco pruebas aprobadas.
Integrar estos controles antes del despliegue permite que sus resultados influyan en lo que se publica. Sin embargo, cero hallazgos describe las reglas, versiones y alcance utilizados; no demuestra seguridad absoluta. Quedan fuera una auditoría completa, pruebas dinámicas y una evaluación SCA como parte de esta comparación. La aplicación guarda notas locales sin cifrado propio y no debe utilizarse para información sensible.
Referencias técnicas
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.