Desarrollo27 de abril de 2026

Automatiza las correcciones de seguridad de paquetes NPM con tareas recurrentes en CI

Cuando desarrollamos con Node.js usamos muchísimos paquetes NPM open source. Cada uno aporta sus ventajas y sus inconvenientes al proyecto. En este artículo veremos:

  • Opciones gratuitas para detectar vulnerabilidades en las dependencias NPM
  • Configuración de Drone CI para lanzar comprobaciones recurrentes
  • Creación automática de Pull Requests con los paquetes corregidos

NPM audit y algo más

Lo primero que viene a la cabeza cuando hablamos de auditar vulnerabilidades es la herramienta NPM audit. Usa un catálogo público de vulnerabilidades para revisar tu proyecto y te propone actualizar las versiones de las librerías para corregir los problemas que encuentre. Tienes más información en el blog oficial de NPM.

Otro informe útil y gratuito que viene de serie es npm outdated. Este comando consulta el registro para ver si alguno de los paquetes instalados está desactualizado. No es una información imprescindible en el día a día, pero conviene tenerla a largo plazo para no caer en la tentación de dejar el proyecto abandonado.


$ npm outdated
Package Current Wanted Latest Location
glob 5.0.15 5.0.15 6.0.1 test-outdated-output
nothingness 0.0.3 git git test-outdated-output
npm 3.5.1 3.5.2 3.5.1 test-outdated-output
local-dev 0.0.3 linked linked test-outdated-output
once 1.3.2 1.3.3 1.3.3 test-outdated-output

Informes automáticos de npm outdated

Estas herramientas son muy útiles, pero, claro, los informes automáticos lo son todavía más. Para ello usamos Drone CI (gratuito y open source) y su nueva funcionalidad, Cron Jobs, para programar tareas recurrentes. Puedes usar cualquier otro CI que prefieras; seguramente ofrecerá lo mismo. Si no conoces Drone CI, echa un vistazo a este artículo de introducción.

Como Drone CI admite varios pipelines, cada informe tiene el suyo y no afecta al principal. Para verlo en conjunto, consulta el ejemplo aquí. Mientras tanto, empecemos con npm outdated.


kind: pipeline
name: npm outdated
steps:
- name: outdated
image: node:10-alpine
commands:
- npm outdated
- name: slack_notification
image: plugins/slack
settings:
webhook: https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX
template: >
NPN detected outdated packages at *{{repo.name}}* for *{{build.branch}}* branch.
Report available by the link {{build.link}}
when:
status:
- failure
trigger:
cron: [weekly]

La sintaxis yaml se explica prácticamente sola. En el primer paso usamos node:10-alpine como imagen base y ejecutamos npm outdated. En el segundo, la notificación de Slack solo se envía si hay algo que actualizar (npm outdated termina con un código de salida de error). Para obtener la URL del webhook de Slack, visita esta página

En las últimas líneas, el pipeline completo se dispara con el Cron Job llamado “outdated”. En nuestros proyectos lo programamos para que se ejecute cada semana, ya que no pensamos actualizar los paquetes cada vez que sale una versión nueva.

Para definir la tarea en Drone, ve a Project -> Settings.

Desde esta interfaz puedes elegir el nombre del job (que se usa para filtrar los pipelines), la rama y la periodicidad, que puede ser cada hora, diaria, semanal, mensual o anual.

npm audit automático y creación de la PR con las correcciones

El comando npm audit revisa tu app en busca de vulnerabilidades y actualiza los paquetes a la versión que haga falta. El pipeline es muy parecido al anterior, pero con un paso extra para crear la PR.


kind: pipeline
name: npm audit
steps:
- name: audit
image: node:10-alpine
commands:
- set -o pipefail && npm audit --force 2>&1 | tee audit.log
- name: audit fix
image: node:10-alpine
commands:
- npm audit fix
when:
status:
- failure
- name: create_fix_pr
image: lnikell/github-hub:2.11.2
environment:
GITHUB_TOKEN:
from_secret: github_token
commands:
- git config --global user.email "email@example.com"
- git config --global user.name "example"
- git checkout -b drone/npm-audit-fix-${DRONE_BUILD_NUMBER}
- git add package.json package-lock.json
- git commit -m 'npm audit fix'
- git push origin drone/npm-audit-fix-${DRONE_BUILD_NUMBER}
- hub pull-request -m "[Security] NPM Audit Fix" -m "$(cat audit.log | tail -2)" -m "${DRONE_BUILD_LINK}"
when:
status:
- failure
- name: slack_notification
image: plugins/slack
settings:
webhook: https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX
template: >
NPN detected vulnerable packages at *{{repo.name}}* for *{{build.branch}}* branch.
Report available by the link {{build.link}}
when:
status:
- failure

En el primer paso usamos la misma imagen node:10-alpine y ejecutamos NPM audit. Además, guardamos los resultados en un archivo audit.log para mostrarlos después en la PR. Si npm audit encuentra paquetes vulnerables, el paso falla y se lanzan el proceso de npm audit fix y la creación de la pull request.


-name: audit fix
image: node:10-alpine
commands:
- npm audit fix
when:
status:
- failure

Para crear la pull request usamos hub, la herramienta de línea de comandos para trabajar con la API de GitHub. Necesitamos generar un Personal Token de GitHub para usarlo en la llamada a la API. Ve a esta página y crea uno nuevo

Selecciona el ámbito de permisos “repo” y añade el token generado a los secrets de Drone con el nombre “github_token”.

Se usa como variable de entorno en el paso siguiente.


- name: create_fix_pr
image: lnikell/github-hub:2.11.2
environment:
GITHUB_TOKEN:
from_secret: github_token
commands:
- git config --global user.email "ci@example.com"
- git config --global user.name "drone"
- git checkout -b drone/npm-audit-fix-${DRONE_BUILD_NUMBER}
- git add package.json package-lock.json
- git commit -m 'npm audit fix'
- git push origin drone/npm-audit-fix-${DRONE_BUILD_NUMBER}
- hub pull-request -m "[Security] NPM Audit Fix" -m "$(cat audit.log | tail -2)" -m "${DRONE_BUILD_LINK}"
when:
status:
- failure

En este paso definimos el patrón para crear la rama y abrimos una pull request con las dos últimas líneas de audit.log. El resultado es una PR bien presentada:

Por último, hay que revisar la parte del trigger del pipeline. Como solo quieres que estas comprobaciones se ejecuten dentro del Cron job, añade lo siguiente:


trigger:
cron: [name_of_the_job]

Eso sí, no te olvides del pipeline principal. Para que no se ejecute durante las tareas Cron, usa la opción exclude así:


trigger:
cron:
exclude: [name_of_the_job]

Conclusión

Este es solo un ejemplo de lo útiles que pueden ser las tareas recurrentes en CI para hacer builds, testear y corregir. Lo configuras una vez y recibes información sobre la seguridad de tu proyecto cada día o cada semana. El enfoque de estos ejemplos se puede adaptar fácilmente a Travis CI o GitLab; si lo haces, comparte tu pipeline.

Cuéntanos tu caso

Escríbenos