NPM Audit: vulnerabilidades bajo la lupa
Analicemos un ejemplo simple de npm audit para ver qué hacer cuando tenemos vulnerabilidades en nuestro proyecto. ¿entrar en pánico? ¡no siempre!
Hola, npm audit
Para quienes ya lo conocen: pueden saltar a la siguiente sección. Para quienes no, rápidamente: npm audit es un comando que podemos correr en cualquier proyecto NPM y que nos informa todos los problemas de vulnerabilidades que existen reportados al día de la fecha, con información para conocer más de cada problema y solucionarlos.
Así descrito se ve como una herramienta genial. Entremos en detalle, porque tiene cosas buenas, y otras sobre las que debemos poner la lupa... ¡te invito a ver un pequeño caso de estudio!
El caso de estudio
Moving motivators es uno de mis side projects que usé para aprender Vue hace un tiempo. Me sirve como área de práctica fuera de los desarrollos más serios. Principalmente porque es un proyecto lo suficientemente pequeño para poder hacer análisis como estos y jugar un poco con el ecosistema de NPM y sus... particularidades 😬.
Al día 21/4/2022 el reporte de npm audit
para este proyecto da lo siguiente:
❯ npm audit
# npm audit report
ansi-regex 3.0.0
Severity: moderate
Inefficient Regular Expression Complexity in chalk/ansi-regex - https://github.com/advisories/GHSA-93q8-gq69-wqmw
fix available via `npm audit fix`
node_modules/log-update/node_modules/ansi-regex
node_modules/string-width/node_modules/ansi-regex
async <2.6.4
Severity: high
Prototype Pollution in async - https://github.com/advisories/GHSA-fwr7-v2mv-hh25
fix available via `npm audit fix`
node_modules/async
2 vulnerabilities (1 moderate, 1 high)
To address all issues, run:
npm audit fix
Ok, al parecer tenemos dos vulnerabilidades. Cada reporte tiene el nombre del paquete vulnerable, la versión que deberíamos actualizar y un link al reporte oficial en Github que explica en detalle la vulnerabilidad. También, nos avisa que podemos arreglarlo con npm audit fix
, que lo que significa es que estamos en condiciones de actualizar el paquete sin romper las reglas de dependencias existentes en el proyecto.
Analizando una de las vulnerabilidades
Veamos ansi-regex entonces. Lo primero es entender qué versión o versiones sabemos que son vulnerables. Vemos entonces el reporte de github que está en el output del comando que ejecutamos previamente, https://github.com/advisories/GHSA-93q8-gq69-wqmw
Luego, vemos qué tan afectados estamos. Usamos npm list ansi-regex
para ver qué tanto dependemos de este paquete y qué caminos de dependencias son los realmente vulnerables:
moving-motivators@0.1.0 /moving-motivators
├─┬ @testing-library/vue@6.5.1
│ └─┬ @testing-library/dom@8.11.3
│ └─┬ pretty-format@27.5.1
│ └── ansi-regex@5.0.1 deduped. // a no preocuparse
├─┬ @vue/cli-service@5.0.0-rc.2
│ ├─┬ html-webpack-plugin@5.3.2
│ │ └─┬ pretty-error@3.0.4
│ │ └─┬ renderkid@2.0.7
│ │ └─┬ strip-ansi@3.0.1
│ │ └── ansi-regex@2.1.1 // a no preocuparse
│ ├─┬ progress-webpack-plugin@1.0.12
│ │ └─┬ log-update@2.3.0
│ │ └─┬ wrap-ansi@3.0.1
│ │ ├─┬ string-width@2.1.1
│ │ │ └─┬ strip-ansi@4.0.0
│ │ │ └── ansi-regex@3.0.0 // vulnerable!
│ │ └─┬ strip-ansi@4.0.0
│ │ └── ansi-regex@3.0.0 // vulnerable!
│ └─┬ webpack-dev-server@4.7.3
│ └─┬ strip-ansi@7.0.1
│ └── ansi-regex@6.0.1 // a no preocuparse
└─┬ eslint@8.8.0
└─┬ strip-ansi@6.0.1
└── ansi-regex@5.0.1 // a no preocuparse
Curiosamente, uno de los caminos nos lleva a la versión 2.1.1 que es una versión bastante antigua pero no es vulnerable. Algo similar a lo ocurrido con los problemas verificados con log4j2, cuando quienes todavía utilizaban la primera versión podían dormir tranquilamente por las noches.
Entonces podemos notar que la parte vulnerable de ansi-regex
se encuentra bajo el paquete progress-webpack-plugin
que es dependencia del paquete de la CLI de Vue... ¡algo que sólo se usa en desarrollo! Entonces, lo que parecía algo grave, pues no lo es tanto.
3 cuestiones a tener en cuenta antes de entrar en pánico
Que se reporte una vulnerabilidad no quiere decir que debemos salir corriendo a solucionarla. Es importante analizar algunos aspectos:
- Por defecto,
npm audit
revisa todas las dependencias: las de producción y las de desarrollo. Tener una vulnerabilidad en el ambiente de desarrollo es muchísimo menos grave que tener una vulnerabilidad en el ambiente de producción. Es por eso que para priorizar y tener una idea de la gravedad, recomiendo ejecutarnpm audit --production
. Este fue el resultado para nuestro proyecto de ejemplo:$ npm audit --production found 0 vulnerabilities
- La vulnerabilidad puede no tener chances de ser explotada. En el ejemplo que vimos, el problema de expresiones regulares ineficientes es un problema común, pero es un problema real si el input que usamos para testear contra esas expresiones regulares supone algo que no podemos controlar. Si el input contiene unos pocos caracteres, entonces no hay mucho de qué preocuparse. Esto dependerá de cada caso...
- Los resultados que se reportan, a veces, incluyen duplicados. Entonces conviene quedarse con el conjunto sin repetidos de los paquetes afectados. Arreglar una dependencia puede bajar en varios ítems el número total de vulnerabilidades si estaba afectando a muchos caminos de diferentes dependencias.
Conclusiones
npm audit
es una herramienta útil, pero no debemos sólo concentrarnos en el número de vulnerabilidades ni en la gravedad que se reporta. Debemos entender cómo es la dependencia que tenemos del paquete en cuestión.- Comparar
npm audit
connpm audit --production
para entender el impacto real de las vulnerabilidades y poder priorizar correctamente. - Usar
npm list
para entender el uso del paquete y los puntos que necesitan actualización. Esto nos puede dar una idea rápida de qué tanto aparece el paquete en las diferentes partes de nuestro proyecto.
¡Recomiendo leer NPM audit: broken by design, que me inspiró a escribir este artículo!
Si tenés alguna anécdota o tip para el mantenimiento de proyectos Node, me gustaría leerla en los comentarios 🤩