En la lección anterior publicamos @escena-viva/formato y vimos que, al hacerlo, adquiríamos un compromiso: otras personas iban a ejecutar nuestro código en sus máquinas. Esta lección da la vuelta al argumento. Cada vez que escribes npm install, estás descargando código de desconocidos y ejecutándolo con tus permisos: los mismos que tiene tu usuario para leer tu ~/.ssh, tu .env o el token de despliegue de tu integración continua.

No es una advertencia teórica. La cadena de suministro de software es hoy uno de los vectores de ataque más rentables, precisamente porque un solo paquete comprometido llega a miles de organizaciones sin que ninguna haya cometido un error. Escena Viva tiene, a día de hoy, dos dependencias directas y un puñado de transitivas: es el momento ideal para aprender a auditarlas, porque el árbol todavía cabe en la cabeza. En el módulo 6 instalaremos Express y el número se multiplicará.

Contenido

  1. La cadena de suministro como superficie de ataque
  2. npm audit: leer la salida de verdad
  3. npm audit fix y el peligro de --force
  4. Localizar al culpable: npm ls y overrides
  5. Ataques que hay que saber reconocer
  6. Medidas prácticas de defensa
  7. Mantenimiento continuo: npm outdated y automatización
  8. Licencias: el riesgo que no es técnico
  9. Auditar Escena Viva y escribir la rutina
  10. Errores Comunes y Consejos
  11. Ejercicios
  12. Conclusión (cierre del Módulo 5)

  1. La cadena de suministro como superficie de ataque

La aritmética del ecosistema npm es la raíz del problema. Un proyecto con 20 dependencias directas suele acabar con entre 300 y 800 paquetes en node_modules, mantenidos por cientos de personas distintas a las que nadie ha verificado. npm ls --all --parseable | wc -l te dice cuántos hay realmente en tu proyecto, y la cifra suele sorprender.

Cada uno de esos paquetes puede, durante la instalación o durante la ejecución:

  • Leer cualquier fichero al que tenga acceso tu usuario, incluidas claves privadas y ficheros .env.
  • Abrir conexiones de red y enviar lo que haya leído a un servidor cualquiera.
  • Ejecutar comandos arbitrarios mediante los ganchos preinstall, install y postinstall que vimos en 05-04.

Ese último punto es el más grave y el menos conocido: no hace falta que llegues a importar el paquete. Un postinstall malicioso se ejecuta durante el propio npm install.

Hay dos ideas que conviene tener claras desde el principio:

Creencia habitual Realidad
"Solo instalo paquetes populares" La popularidad protege de la mala calidad, no del secuestro de una cuenta
"Es una dependencia de desarrollo, no llega a producción" Se ejecuta en tu portátil y en tu CI, donde están los tokens
"Reviso las dependencias directas" El 95 % del código instalado es transitivo
"El lock me protege" Te protege de cambios inesperados, no de que la versión fijada sea vulnerable

  1. npm audit: leer la salida de verdad

npm audit compara el árbol de dependencias resuelto (el del package-lock.json) con una base de datos pública de vulnerabilidades conocidas.

npm audit

Una salida típica tiene esta forma:

# npm audit report

tar-fs  2.0.0 - 2.1.2
Severity: high
tar-fs can extract outside the specified directory - GHSA-pq67-2wwv-3xjx
fix available via `npm audit fix`
node_modules/tar-fs
  bundled-dependency  *
  Depends on vulnerable versions of tar-fs
  node_modules/bundled-dependency

2 vulnerabilities (1 moderate, 1 high)

Hay que leerla por partes: tar-fs 2.0.0 - 2.1.2 es el paquete afectado y el rango vulnerable; Severity la gravedad asignada; la descripción con el identificador GHSA-... dice qué se puede hacer con el fallo (este dato importa más que la gravedad); y el bloque indentado muestra la cadena de dependencias, donde bundled-dependency es quien arrastra el paquete vulnerable y, por tanto, donde hay que actuar.

Las gravedades no son una escala de urgencia automática:

Gravedad Significado aproximado Reacción razonable
Critical Ejecución remota de código, robo de credenciales Inmediata, aunque sea viernes
High Escalada, escritura fuera de ruta, evasión de autenticación En días
Moderate Denegación de servicio, filtración limitada En la siguiente iteración
Low Impacto marginal o muy condicionado Cuando toque mantenimiento

Lo importante es que la gravedad se calcula en abstracto, sin saber cómo usas tú el paquete. Una vulnerabilidad "high" de expresión regular con denegación de servicio en una librería que solo procesa cadenas escritas por ti, en tiempo de compilación, no es una emergencia. Y a la inversa: una "moderate" en el camino de una petición HTTP pública puede ser urgente. Antes de reaccionar, pregúntate siempre: ¿el dato que llega a esa función viene de un usuario?

Sobre los falsos positivos en dependencias de desarrollo: es habitual que npm audit señale docenas de problemas en la cadena de herramientas de construcción o pruebas. Merecen atención, pero de otra clase: un fallo de denegación de servicio en un formateador que solo se ejecuta en tu portátil no es un riesgo para tus usuarios. Para separar el ruido de la señal, npm audit --omit=dev muestra solo lo que llega a producción y npm audit --audit-level=high solo lo que pasa de cierta gravedad. Esta última es la que conviene poner en la integración continua: fallar la construcción por cualquier "low" en una herramienta de desarrollo genera fatiga de alertas, y la fatiga de alertas es cómo se acaban ignorando las críticas. Para procesar la salida con herramientas, npm audit --json la devuelve estructurada.

  1. npm audit fix y el peligro de --force

npm audit fix intenta resolver las vulnerabilidades respetando los rangos declarados en tu package.json. Si dotenv está como ^17.2.1 y el arreglo está en 17.2.4, lo aplica sin preguntar: es un cambio compatible según SemVer. Es una operación segura y conviene ejecutarla con regularidad.

El problema es el flag que la salida te sugiere cuando no puede arreglar algo. npm audit fix --force abandona los rangos declarados e instala la versión que resuelve el problema, aunque sea un salto mayor: puede cambiar express@4 por express@5 o subir dos versiones mayores una herramienta de construcción, con todos los cambios rompedores que eso implique. La salida lo avisa entre líneas:

npm WARN audit Updating express to 5.1.0, which is a SemVer major change.

La regla es sencilla: npm audit fix sí, en cualquier momento; npm audit fix --force nunca a ciegas, y jamás en la rama principal sin revisar el git diff del package.json y del lock, y sin pasar las pruebas. Si lo ejecutas por error:

git checkout package.json package-lock.json
npm ci

Y hay un tercer caso, el más frustrante: npm audit informa de una vulnerabilidad sin arreglo disponible, porque el paquete intermedio no ha publicado una versión que use la dependencia parcheada. Ahí es donde entran las dos herramientas de la sección siguiente.

  1. Localizar al culpable: npm ls y overrides

Antes de arreglar nada hay que saber quién trae el paquete vulnerable. npm ls lo responde:

npm ls tar-fs
[email protected] /home/usuario/escena-viva
└─┬ [email protected]
  └─┬ [email protected]
    └── [email protected]

La lectura es directa: no dependemos de tar-fs, sino de herramienta-construccion, que usa empaquetador, que usa tar-fs. Las opciones, por orden de preferencia: actualizar la dependencia directa si ya existe una versión que arrastre la parcheada (lo limpio); forzar la versión de la transitiva con overrides si lo anterior no está disponible; o sustituir la dependencia directa si el mantenedor no responde.

El campo overrides del package.json permite a npm reescribir el árbol resuelto:

{
  "overrides": {
    "tar-fs": "2.1.3"
  }
}

Eso obliga a que cualquier aparición de tar-fs en el árbol, venga de donde venga, se resuelva a 2.1.3. Si quieres ser más quirúrgico y afectar solo a una rama concreta:

{
  "overrides": {
    "herramienta-construccion": {
      "empaquetador": {
        "tar-fs": "2.1.3"
      }
    }
  }
}

Tras editar el manifiesto hay que regenerar el árbol y comprobar el resultado:

npm install
npm ls tar-fs
npm audit

Dos advertencias sobre overrides. La primera: estás instalando una combinación de versiones que el mantenedor de empaquetador no ha probado nunca, así que necesitas pruebas propias que respalden el cambio (módulo 9). La segunda: un overrides es deuda técnica con fecha. Ponle un comentario en el CHANGELOG o en el README explicando por qué está ahí y cuándo se podrá quitar, o dentro de un año nadie se atreverá a tocarlo.

  1. Ataques que hay que saber reconocer

Typosquatting. Se registra un paquete con un nombre casi idéntico a uno popular —expres en vez de express, lodahs en vez de lodash, crossenv en vez de cross-env— y se espera a que alguien se equivoque al teclear o al copiar de un tutorial mal escrito. El paquete falso suele reexportar el auténtico —para que todo "funcione"— y añadir un postinstall que envía tus variables de entorno a un servidor externo. Defensa: copia los nombres desde la documentación oficial, no desde un blog, y desconfía de un paquete con 200 descargas cuando el que buscas tiene millones.

Secuestro de la cuenta de un mantenedor. El paquete es legítimo y lleva años siendo de fiar; lo que cambia es quién controla la cuenta que publica. Ha ocurrido por contraseñas reutilizadas, por phishing dirigido a mantenedores y por transferencias de propiedad a desconocidos que se ofrecían amablemente a "ayudar con el mantenimiento". La versión maliciosa se publica y se propaga en horas a través de los rangos ^. Defensa: npm ci con lock fijado, no actualizar a ciegas el día de publicación, y revisar el diff cuando un paquete pequeño y estable saca de pronto una versión.

Paquetes abandonados. No hay ataque, hay ausencia: nadie parchea, nadie responde a las incidencias, y las vulnerabilidades se acumulan. Señales de alarma: último publish hace tres años, incidencias abiertas sin respuesta, un único mantenedor. Es también el terreno fértil del secuestro por transferencia. Defensa: la lista de comprobación de 05-02, aplicada también en las revisiones periódicas.

Confusión de dependencias. Una empresa usa un paquete interno llamado, por ejemplo, escena-viva-pagos, alojado en su registro privado. Un atacante publica un paquete con ese mismo nombre en el registro público y con una versión más alta. Según cómo esté configurada la resolución, el cliente puede preferir la versión pública. Defensa: publicar siempre bajo un ámbito propio (@escena-viva/pagos) y anclar ese ámbito al registro privado:

# .npmrc del proyecto
@escena-viva:registry=https://registro.interno.escena-viva.test/

Así, cualquier cosa bajo @escena-viva/ se busca solo en el registro interno, y un homónimo público es irrelevante.

Protestware. El mantenedor introduce deliberadamente un comportamiento no funcional —mensajes políticos por consola, retrasos, en casos extremos borrado de ficheros según la geolocalización— como forma de protesta. No es un ataque externo, pero rompe la confianza igual y ha ocurrido en paquetes con millones de descargas semanales. Defensa: la misma que para el resto —lock fijado, actualizaciones revisadas— más una reflexión honesta sobre cuántas dependencias triviales tiene tu proyecto.

  1. Medidas prácticas de defensa

npm ci en producción y en CI. Ya lo vimos en 05-03: instala exactamente lo que dice el package-lock.json, sin resolver rangos. Es lo que impide que una versión publicada hace diez minutos entre en tu despliegue.

npm ci --omit=dev

--ignore-scripts. npm ci --ignore-scripts impide que se ejecuten los ganchos de instalación de las dependencias, que es por donde entra buena parte del código malicioso. Qué rompe: los paquetes que compilan binarios nativos o descargan artefactos en su postinstall quedan inservibles —librerías con extensiones nativas, herramientas que bajan un navegador—. Escena Viva, con dotenv y prettier, no tiene ninguno, así que puede activarlo sin problema y dejarlo fijado en el .npmrc del proyecto:

engine-strict=true
ignore-scripts=true

Si un paquete concreto lo necesita, se ejecuta su script de forma explícita y consciente en vez de abrir la puerta a los cientos restantes.

Fijar versiones críticas. Para lo que toca autenticación, criptografía o pagos, la comodidad de ^ no compensa. Fija la versión exacta y actualiza a mano leyendo el changelog:

"dependencies": {
  "@escena-viva/formato": "0.2.1"
}

Revisar el package-lock.json en las revisiones de código. No hay que leer las 4.000 líneas; hay que mirar tres cosas: paquetes nuevos que nadie ha mencionado en la descripción del cambio, cambios en el campo resolved que apunten a un dominio que no sea el registro esperado, y saltos de versión mayor. Un lock que cambia en un pull request que "solo toca CSS" es una señal.

Tokens de solo lectura. Si tu CI únicamente instala, su token no necesita permiso de publicación. El token con permiso de escritura vive solo en el flujo que publica, y con alcance limitado a los paquetes concretos (lo vimos en 05-05, y lo configuraremos en el módulo 11).

npm audit signatures. Verifica que los paquetes instalados están firmados por el registro y que las firmas coinciden con lo descargado; detecta manipulaciones en tránsito o en un espejo comprometido. Responde con un 214 packages have verified registry signatures en pocos segundos, y es un buen candidato para el flujo de CI junto a npm audit --audit-level=high.

  1. Mantenimiento continuo: npm outdated y automatización

La seguridad de dependencias no es una tarea, es una rutina. La herramienta base:

npm outdated
Package    Current  Wanted  Latest  Location              Depended by
dotenv      17.2.1  17.2.4  17.4.0  node_modules/dotenv   escena-viva
prettier     3.6.2   3.6.4   4.0.1  node_modules/prettier escena-viva

Current es lo instalado ahora mismo, Wanted lo máximo que permite tu rango en package.json y Latest la última publicada con la etiqueta latest. La diferencia entre Current y Wanted se resuelve con npm update y es de bajo riesgo; la diferencia entre Wanted y Latest es un salto mayor y requiere trabajo: leer el changelog, migrar, probar.

Una política de actualización razonable, y sostenible:

Tipo de cambio Plazo Procedimiento
Parche de seguridad (alta o crítica) El mismo día npm audit fix, pruebas, despliegue
Parche normal Semanal npm update, pruebas automáticas, fusionar
Menor Quincenal o mensual Revisar changelog, pruebas, fusionar
Mayor Planificado Rama propia, lectura de la guía de migración, pruebas manuales

Todo esto descansa sobre una condición que hoy no cumplimos: pruebas automáticas fiables. Sin ellas, actualizar es una apuesta y el equipo acaba congelando versiones por miedo, que es la peor situación posible. Por eso el test de Escena Viva sigue en exit 1 con el mensaje "Sin pruebas todavia — Modulo 9": es una deuda declarada, no olvidada, y es la que hará que este calendario sea aplicable.

Para no hacerlo a mano existen los bots de actualización, Dependabot (integrado en GitHub) y Renovate. Abren pull requests automáticos cuando hay versiones nuevas, con el changelog en la descripción, y dejan que tu CI decida si pasan. Una configuración sensata:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    open-pull-requests-limit: 5
    groups:
      parches:
        update-types: [patch]

Agrupar los parches en un solo pull request semanal y dejar los mayores individuales evita el efecto contrario al deseado: veinte pull requests abiertos que nadie revisa son peor que ninguno.

  1. Licencias: el riesgo que no es técnico

Cada dependencia trae una licencia, y esa licencia impone condiciones a tu producto. En un proyecto personal se ignora; en un proyecto de empresa es un asunto legal con consecuencias reales.

Licencia Tipo Implicación práctica
MIT, ISC, BSD, Apache-2.0 Permisiva Uso libre, incluso comercial y cerrado, conservando el aviso de copyright
LGPL Copyleft débil Uso posible enlazando, con condiciones si modificas la librería
GPL, AGPL Copyleft fuerte Puede obligar a publicar el código de tu producto; AGPL alcanza también al software servido por red
Sin licencia Todos los derechos reservados Legalmente no tienes permiso de uso, aunque esté en npm
Comercial / dual Variable Puede requerir compra a partir de cierto tamaño de empresa

El caso que más problemas causa en aplicaciones web es AGPL: como Escena Viva sirve su funcionalidad por red, una dependencia AGPL podría obligar a publicar el código completo del proyecto. Y "sin licencia" es peor que una licencia restrictiva, porque no hay nada que negociar.

Revisar las licencias del árbol es sencillo: npm view dotenv license muestra la declarada por un paquete, y npx license-checker --summary recorre el árbol completo. En un entorno de empresa lo habitual es tener una lista de licencias aprobadas y comprobarla en CI. La conversación con el departamento legal es infinitamente más fácil antes de construir el producto que después.

  1. Auditar Escena Viva y escribir la rutina

Vamos a aplicar todo lo anterior a nuestro proyecto. Las dependencias reales son @escena-viva/formato y dotenv en producción, prettier y eslint en desarrollo.

npm ls --all            # 1. Estado del arbol completo
npm audit               # 2. Auditoria general...
npm audit --omit=dev    #    ...y solo lo que llega a produccion
npm audit signatures    # 3. Verificacion de firmas del registro
npm outdated            # 4. Que hay desactualizado
npm view dotenv license # 5. Licencias de las dependencias directas

El resultado esperado es tranquilizador precisamente porque durante los módulos 1 a 4 construimos el servidor entero con la biblioteca estándar. Ese fue el mejor control de seguridad de todo el curso: el código que no instalas no puede comprometerte.

Con el diagnóstico hecho, dejamos la rutina escrita donde se pueda encontrar. Añadimos esta sección al README.md del proyecto:

## Mantenimiento de dependencias

**Semanal** (responsable: quien haga la guardia)
- `npm outdated` y `npm audit`
- `npm audit fix` para lo que se resuelva dentro de los rangos
- Revisar y fusionar el PR agrupado de parches de Dependabot

**Mensual**
- Revisar versiones menores pendientes leyendo su changelog
- Revisar que ningun `overrides` siga siendo necesario
- `npm ls --all` y comprobar si ha aparecido alguna dependencia sin justificar

**Ante un aviso de gravedad alta o critica**
- `npm ls <paquete>` para identificar quien lo arrastra
- Actualizar la dependencia directa; si no hay version, usar `overrides`
- Nunca `npm audit fix --force` sin revisar el diff y pasar las pruebas

**Reglas fijas**
- En produccion y en CI: `npm ci --omit=dev`, nunca `npm install`
- `ignore-scripts=true` en el `.npmrc` del proyecto
- El `package-lock.json` se revisa en cada pull request
- Antes de aceptar una dependencia nueva: lista de comprobacion (mantenimiento,
  descargas, dependencias propias, licencia, alternativa en la biblioteca estandar)

Y añadimos un script que agrupa la comprobación, siguiendo el criterio de 05-04 de que todo se ejecute por la misma interfaz:

"scripts": {
  "auditoria": "npm audit --audit-level=high && npm audit signatures",
  "comprobar": "npm run lint && npm run format && npm run auditoria"
}

Ahora npm run auditoria forma parte del vocabulario del proyecto, y npm run comprobar —el comando que ya usaba el equipo— incorpora la seguridad sin que nadie tenga que recordar un comando nuevo.

Errores Comunes y Consejos

Ejecutar npm audit fix --force porque la propia salida lo sugiere. Ese texto no evalúa tu proyecto: cambia versiones mayores sin preguntar. Trátalo como una migración, con rama, revisión y pruebas.

Tratar todas las gravedades por igual. Una crítica en el camino de una petición pública y una moderada en un formateador de desarrollo no merecen la misma reacción. Lee la descripción, no solo la etiqueta.

Ignorar npm audit porque "siempre sale lo mismo". La fatiga de alertas es cómo se cuelan las importantes. Filtra con --audit-level=high y --omit=dev hasta que la salida vuelva a ser accionable.

Creer que el lock es una defensa de seguridad. Garantiza reproducibilidad: instalarás siempre lo mismo, vulnerable o no. La defensa es auditar, no fijar. Y en despliegue siempre npm ci, nunca npm install, que resuelve rangos y puede traerte una versión publicada hace minutos.

Copiar nombres de paquetes desde blogs o desde una respuesta generada. Es la vía natural del typosquatting. Verifica el nombre en la documentación oficial y mira las descargas antes de instalar.

Publicar paquetes internos sin ámbito. Es la puerta abierta a la confusión de dependencias. Usa @empresa/nombre y ancla el ámbito a tu registro en el .npmrc.

Dejar overrides sin documentar. En seis meses nadie sabrá si aquello sigue haciendo falta y nadie se atreverá a quitarlo. Anota el motivo y la condición de salida.

Descartar las licencias como "cosa de abogados". Una AGPL en el árbol de una aplicación web puede tener consecuencias caras. Cuesta menos comprobarlo hoy.

Ejercicios

Ejercicio 1: interpretar una auditoría

npm audit en Escena Viva informa de esto:

minimatch  <3.0.5
Severity: high
Regular Expression Denial of Service - GHSA-f8q6-p94x-37v3
No fix available
node_modules/minimatch
  eslint  8.0.0 - 8.57.0
  Depends on vulnerable versions of minimatch

Responde: ¿es una urgencia para los usuarios de la plataforma? ¿Qué comando usas para confirmar quién arrastra minimatch? Enumera las opciones de resolución, ordenadas de mejor a peor, sabiendo que la salida dice "No fix available".

Ejercicio 2: forzar una transitiva parcheada

Una dependencia directa tuya, [email protected], arrastra [email protected], que tiene una vulnerabilidad crítica ya corregida en [email protected]. El mantenedor de generador-informes no ha publicado nada en seis meses. Escribe el overrides mínimo que resuelve el caso afectando solo a esa rama, los comandos para aplicarlo y verificarlo, y qué anotarías en el README.

Ejercicio 3: un pull request sospechoso

Llega un pull request titulado "Mejora del formato del informe de ocupación". El diff toca src/informes/ocupacion.js (12 líneas), pero también añade en package.json una dependencia colores-consola y 340 líneas al package-lock.json. Enumera las comprobaciones concretas que harías antes de aprobarlo y qué señales concretas te harían rechazarlo.

Soluciones

Solución 1. No es una urgencia para los usuarios. eslint es una dependencia de desarrollo: no se instala en producción (npm ci --omit=dev) ni procesa datos que envíe un comprador de entradas. La denegación de servicio por expresión regular requiere una cadena de entrada maliciosa, y aquí las entradas son rutas de ficheros del propio repositorio. Es un hallazgo real pero de riesgo bajo en este contexto; npm audit --omit=dev debería dejar de mostrarlo.

Para confirmar la cadena:

npm ls minimatch

Opciones, de mejor a peor:

  1. Actualizar eslint a una versión mayor que ya use un minimatch parcheado. Es la solución limpia y afecta solo a las herramientas.
  2. Si esa versión aún no existe, añadir un overrides para minimatch y documentar que es temporal.
  3. Aceptar el riesgo de forma explícita y documentada, con revisión en la rutina mensual, dado que es una dependencia de desarrollo.
  4. Sustituir eslint. Desproporcionado para el riesgo real.

Lo que no se hace es npm audit fix --force, que probablemente subiría ESLint de versión mayor y rompería la configuración de 05-04 sin previo aviso.

Solución 2. El overrides acotado a esa rama:

{
  "overrides": {
    "generador-informes": {
      "analizador-csv": "1.0.5"
    }
  }
}

Aplicación y verificación:

npm install
npm ls analizador-csv   # debe mostrar 1.0.5
npm audit               # la critica debe desaparecer
npm run informe         # comprobacion funcional real

En el README, dentro de la sección de mantenimiento:

### Overrides activos
- `[email protected]` bajo `generador-informes`: parche de GHSA-xxxx.
  Retirar cuando `generador-informes` publique una version que ya lo incluya.
  Revisado: 2026-08-14.

La anotación con fecha es lo que convierte una solución temporal en algo revisable, en vez de en un misterio permanente.

Solución 3. Comprobaciones antes de aprobar:

  1. ¿Está justificada la dependencia? Doce líneas de formato de informe no suelen necesitar un paquete externo; las secuencias ANSI de color caben en cinco líneas propias, y Node ya ofrece utilidades de estilo en consola.
  2. Verificar el nombre exacto contra la documentación oficial del paquete que se pretendía usar: es el patrón exacto del typosquatting.
  3. npm view colores-consola: fecha de la última publicación, número de descargas, mantenedores, licencia y —muy importante— sus propias dependencias.
  4. Buscar postinstall en el package.json del paquete y en el lock.
  5. Revisar en el package-lock.json los campos resolved: deben apuntar al registro esperado y no a un dominio o repositorio arbitrario.
  6. Comprobar cuántos paquetes nuevos aparecen: 340 líneas de lock para una utilidad de color sugiere un árbol transitivo desproporcionado.
  7. Pasar npm audit y npm audit signatures sobre la rama.

Señales para rechazar directamente: el nombre no coincide con ningún paquete conocido y tiene pocas descargas; hay un script de instalación; el resolved apunta fuera del registro; el paquete se publicó hace días; o simplemente la funcionalidad se resuelve sin dependencia. En este caso, lo razonable es pedir que el color se implemente en el propio proyecto y que el pull request quede en las 12 líneas que anunciaba su título.

Conclusión

Instalar es ejecutar código ajeno con tus permisos. Esa frase resume la lección y justifica todo lo demás: npm audit para saber qué hay, leyendo la descripción del fallo y no solo la etiqueta de gravedad; npm audit fix como rutina y --force nunca a ciegas; npm ls <paquete> para localizar quién arrastra lo vulnerable y overrides —documentado y con fecha— para forzar una transitiva parcheada cuando no hay salida limpia. Hemos aprendido a reconocer typosquatting, cuentas secuestradas, paquetes abandonados, confusión de dependencias y protestware, y a defendernos con medidas concretas: npm ci en producción, ignore-scripts en el .npmrc, versiones fijadas en lo crítico, revisión del lock en cada pull request, tokens de solo lectura y npm audit signatures. Y hemos dejado escrita en el README una rutina de mantenimiento —parches rápido, mayores con calma— que solo será sostenible cuando tengamos las pruebas del módulo 9, además de una mirada a las licencias, que son un riesgo de empresa aunque no sean un riesgo técnico.

Con esto se cierra el módulo 5 completo. Empezamos con el manifiesto: npm init, el package.json campo a campo, node_modules, npx y .npmrc. Seguimos con la instalación: dependencies frente a devDependencies, el árbol aplanado y sus dependencias fantasma, y la lista de comprobación para aceptar una dependencia antes de instalarla. Después vino el versionado: SemVer, los rangos y su trampa en ^0.x.y, el package-lock.json con su integrity y su resolved, y npm ci como forma correcta de instalar en máquinas que no son la tuya. Luego los scripts, convertidos en la interfaz única del proyecto, con el PATH de node_modules/.bin, los ganchos y sus riesgos. En la quinta lección cambiamos de lado del mostrador y publicamos @escena-viva/formato, decidiendo con exports qué es API pública y con files qué viaja al registro. Y aquí hemos aprendido a convivir con todo ese código ajeno sin ingenuidad. Escena Viva ya no es solo una aplicación que funciona: es un proyecto con manifiesto, versiones reproducibles, comandos estandarizados, un paquete propio publicado y una política de seguridad escrita.

En el módulo 6 llega Express, y llega en el mejor momento posible. Vamos a sustituir el enrutador artesanal que escribimos en el módulo 4 por un framework, y la experiencia va a ser distinta de la habitual: donde otros ven magia, tú vas a reconocer piezas. El app.get('/api/eventos/:id') de Express es el enrutador.js que construiste comparando rutas y extrayendo parámetros a mano. Su middleware es la cadena de funciones que ya encadenaste alrededor de cuerpo.js. Su res.json() es tu respuestas.js. Su manejador de errores es tu errores-http.js. Y cada dependencia que Express traiga consigo la vas a mirar con los ojos que acabas de entrenar en esta lección. Empezaremos por la introducción a Express: qué problema resuelve exactamente, qué te ahorra y qué sigue siendo responsabilidad tuya.

Curso de Node.js: De Principiante a Avanzado

Módulo 1: Introducción a Node.js

Módulo 2: Conceptos Básicos

Módulo 3: Sistema de Archivos y E/S

Módulo 4: HTTP y Servidores Web

Módulo 5: NPM y Gestión de Paquetes

Módulo 6: Framework Express.js

Módulo 7: Bases de Datos y ORMs

Módulo 8: Autenticación y Autorización

Módulo 9: Pruebas y Depuración

Módulo 10: Temas Avanzados

Módulo 11: Despliegue y DevOps

Módulo 12: Proyectos del Mundo Real

© Copyright 2026. Todos los derechos reservados