Hay un tipo de archivo que casi siempre pasa debajo del radar porque parece “solo configuración”. Lo abres, cambias una variable, guardas y sigues con tu día. El problema es que, en varios ecosistemas modernos, ese archivo no solo describe comportamiento: también puede ejecutar código, cargar plugins, disparar hooks o activar transformaciones que terminan en la cadena de suministro.
Ese es el punto ciego. Mucha gente revisa dependencias, contenedores, secretos y pipelines, pero deja fuera los configs que tienen capacidad de ejecución. Y cuando eso ocurre, el atacante no necesita tocar tu aplicación principal. Le basta con meter mano en un archivo que tu tooling ya confía, como package.json, .npmrc, .yarnrc.yml, .prettierrc, vite.config.ts, eslint.config.js, babel.config.js, jest.config.js o archivos de CI que interpretan scripts.
Qué significa que un config ejecute código
No todos los archivos de configuración son pasivos. Algunos son solo datos, como un YAML con parámetros. Otros son ejecutables por diseño: JavaScript en configs de build, scripts en package managers, hooks de linters, transformaciones de test runners o loaders que se evalúan durante la instalación o el arranque.
En la práctica, eso quiere decir que el config tiene dos roles al mismo tiempo. Por un lado define comportamiento. Por otro, puede introducir lógica arbitraria que se ejecuta con los permisos del proceso que lo lee. Si ese proceso corre en tu laptop, en tu CI o dentro del runner de un release, el alcance cambia mucho.
Un ejemplo sencillo: un archivo de configuración de ESLint en formato JavaScript puede importar módulos, leer variables de entorno y construir reglas dinámicamente. Eso es útil para monorepos y setups complejos. Pero también significa que, si alguien logra modificar ese archivo o una dependencia que él importa, puede ejecutar código cuando el linter arranca.
Dónde aparece el riesgo de verdad
El riesgo no está solo en el archivo en sí. Aparece cuando el archivo se evalúa automáticamente y confías en contenido que puede venir de fuera: una dependencia nueva, un plugin, una plantilla, una contribución de un tercero o una actualización aparentemente menor.
En seguridad de la cadena de suministro, ese detalle importa porque el atacante busca el camino de menor resistencia. A veces no ataca el binario final. Ataca el paso previo, donde un config ejecutable le da una puerta de entrada más silenciosa.
Configs que suelen sorprender
Estos son algunos casos típicos donde el equipo cree que está viendo “config” y en realidad está ejecutando lógica:
package.jsoncon scripts,preinstall,postinstalloprepare.yarnrc.ymlo.npmrccon comportamiento que afecta instalación y resoluciónvite.config.ts,webpack.config.js,next.config.jsorollup.config.jseslint.config.jsy configuraciones de linters en formato JSjest.config.js,playwright.config.tsocypress.config.ts- archivos de CI como workflows que invocan scripts del repo
No todos son inseguros por definición. El problema es asumir que, por llamarse config, solo contienen datos.
Cómo se amplía la superficie de ataque
Cuando un config ejecuta código, la superficie de ataque deja de ser solo la aplicación. También incluye el entorno donde ese config corre, las dependencias que importa y los pasos automáticos que lo disparan. Eso cambia el mapa de amenazas para devs y para plataforma.
Piensa en un pipeline que instala dependencias, ejecuta linters, corre tests y genera build. Si alguno de esos pasos carga un config ejecutable, el atacante puede buscar una modificación en una dependencia transitiva, un plugin de terceros o un archivo de configuración versionado. Ya no necesita un exploit sofisticado sobre tu app. Solo necesita que el proceso confíe en algo que no revisas con el mismo nivel de detalle.
La otra parte del problema es la repetición. Un config ejecutable suele correr muchas veces: en cada npm install, en cada commit, en cada job de CI. Eso le da al atacante más oportunidades y a ti menos margen para notar un comportamiento raro.
Tres zonas donde el riesgo se multiplica
- Instalación de dependencias: scripts como
preinstallopostinstallpueden ejecutarse automáticamente durante el setup. - Build y test: configs de bundlers, runners o linters pueden importar módulos y ejecutar lógica al iniciar.
- CI/CD: si el pipeline toma decisiones con archivos del repo, un cambio pequeño puede afectar releases, artefactos o secretos expuestos al job.
La documentación oficial de npm sobre scripts deja claro que varios lifecycle scripts pueden ejecutarse durante instalación y publicación, así que vale la pena revisarla si tu equipo depende de ese flujo: https://docs.npmjs.com/cli/v10/using-npm/scripts
Casos reales de por qué esto importa
La historia de la seguridad de software ya mostró varias veces que el problema no siempre está en el código de negocio. A veces está en el mecanismo de entrega, en el package manager o en un archivo que el ecosistema interpreta como instrucciones.
Un ejemplo cercano es el abuso de lifecycle scripts en entornos Node.js. No hace falta que te roben el código fuente completo para causar daño. Si un paquete o un config logra ejecutar algo durante instalación, puede leer variables de entorno, tocar archivos locales o preparar una segunda etapa. Por eso tantas guías modernas recomiendan reducir al mínimo los scripts automáticos en instalaciones reproducibles.
Otro caso común es el de herramientas de build que aceptan configuración en JavaScript o TypeScript. Eso da flexibilidad, pero también abre la puerta a ejecutar lógica antes de compilar. En equipos grandes, esa lógica suele crecer sin mucho control porque “solo resuelve un detalle del monorepo”. Con el tiempo, ese detalle se convierte en una dependencia crítica.
Ejemplo práctico en un repo Node.js
Imagina este escenario:
- tu repo usa npm
- el setup corre
npm installen CI - existe un paquete interno o externo con
postinstall - el pipeline tiene acceso a variables de entorno sensibles
Si ese paquete cambia o se compromete, el script puede ejecutarse en el momento más conveniente para el atacante: cuando el entorno ya tiene credenciales cargadas y todavía no generaste el artefacto final. No hace falta un payload complejo. A veces basta con exfiltrar un token o alterar un archivo de build.
La documentación oficial de GitHub sobre seguridad en Actions también insiste en limitar permisos y tratar el contenido del repositorio como no confiable en ciertos contextos: https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions
Cómo detectar configs que ejecutan código
No necesitas convertirte en analista forense para empezar a ver este riesgo. Sí necesitas una revisión más mecánica de tus archivos y de los puntos donde tu tooling interpreta código dentro de un config.
Primero, identifica qué archivos en tu stack no son simples datos. En muchos repos, el problema empieza con extensiones como .js, .ts, .mjs o .cjs usadas como configuración. Luego revisa qué importan esos archivos, qué scripts invocan y qué se ejecuta automáticamente en instalación, build o test.
Checklist de revisión rápida
- Busca configs en JavaScript o TypeScript.
- Revisa si importan dependencias externas o internas.
- Identifica hooks automáticos de instalación y publicación.
- Marca los pasos de CI que leen configs del repo.
- Verifica si hay acceso a secretos, tokens o credenciales en ese momento.
- Pregunta si ese archivo necesita lógica o si puede ser datos estáticos.
Si quieres profundizar en el comportamiento de npm en scripts y lifecycle hooks, la guía oficial sigue siendo la referencia base: https://docs.npmjs.com/cli/v10/using-npm/scripts
Señales de alerta que sí valen tu tiempo
Hay patrones que merecen una revisión inmediata:
- configs que hacen
importde paquetes poco conocidos postinstalloprepareen dependencias que no esperabas- lógica condicional que depende de
process.env - archivos de configuración que cambian seguido sin motivo funcional claro
- uso de
eval,Function,child_processo llamadas a shell dentro de configs
No se trata de prohibir todo. Se trata de saber qué piezas tienen capacidad de ejecución y tratarlas como código sensible, no como metadata inocente.
Qué puede hacer tu equipo para reducir el riesgo
La buena noticia es que este punto ciego se puede cerrar bastante con medidas concretas. No necesitas rediseñar toda tu plataforma desde cero. Sí necesitas poner límites claros a qué configs pueden ejecutar lógica y en qué entornos.
La primera medida es reducir la superficie. Si un archivo puede ser puro dato, que lo sea. Si necesitas comportamiento dinámico, encapsúlalo en un módulo pequeño y revisable. Cuanto menos lógica escondida haya en configs, más fácil es auditar cambios y detectar anomalías.
La segunda medida es controlar la ejecución automática. En entornos CI, usa instalaciones reproducibles y revisa si puedes desactivar scripts donde no sean necesarios. En Node.js, por ejemplo, muchas organizaciones limitan scripts de instalación en jobs que solo validan código o generan artefactos intermedios.
Medidas concretas para devs y plataforma
- Mantén configs ejecutables en el mínimo número posible.
- Separa datos estáticos de lógica dinámica.
- Revisa cada
postinstall,prepareo hook automático. - Ejecuta CI con permisos mínimos y secretos segmentados.
- Bloquea dependencias no aprobadas o demasiado opacas.
- Observa cambios en configs como si fueran cambios en código crítico.
- Añade revisión obligatoria para archivos que puedan ejecutar lógica.
Tabla de impacto por tipo de config
| Tipo de archivo | Puede ejecutar código | Riesgo típico | Qué revisar primero |
|---|---|---|---|
package.json | Sí, vía scripts | Instalación y publicación | preinstall, postinstall, prepare |
vite.config.ts | Sí | Build y plugins | Imports, plugins y acceso a entorno |
eslint.config.js | Sí | Lógica en linting | Módulos importados y condiciones dinámicas |
jest.config.js | Sí | Ejecución de tests | Transformaciones, setup files y resolvers |
.yarnrc.yml | A veces indirecto | Instalación y resolución | Comportamiento del package manager |
workflow.yml de CI | Sí, vía comandos | Pipeline y secretos | Pasos shell, permisos y variables expuestas |
Si tu organización usa monorepos, este control es todavía más importante. Un cambio pequeño en un config compartido puede afectar decenas de paquetes o apps al mismo tiempo.
Cómo lo aterrizamos en un equipo real
Supón que trabajas en un equipo de plataforma que mantiene plantillas para varios servicios. El objetivo no es eliminar toda flexibilidad, porque eso frena a desarrollo. El objetivo es definir una política simple: qué configs pueden ejecutar código, quién los aprueba y bajo qué condiciones corren.
Una forma práctica de hacerlo es clasificar archivos en tres grupos: datos puros, configs ejecutables controlados y excepciones. Los datos puros no deberían cargar módulos ni llamar procesos externos. Los configs ejecutables controlados pueden hacerlo, pero con revisión adicional. Las excepciones se documentan con fecha, dueño y motivo.
Luego, automatiza la detección. Un linter de políticas o un script de revisión puede alertarte si aparece un postinstall, un eval o un import inesperado en un archivo de configuración. No hace falta capturar todo al 100 por ciento desde el día uno. Con que evites los casos más obvios ya reduces bastante el riesgo.
Un enfoque operativo en 4 pasos
- Inventaria configs ejecutables en tus repos y templates.
- Marca cuáles corren en laptop, CI y release.
- Define una lista de patrones prohibidos o de revisión obligatoria.
- Agrega controles en PR y en pipeline para cambios sensibles.
Si tu stack depende mucho de Node.js, vale la pena revisar también cómo se comportan los package managers modernos y qué scripts activan por defecto. La combinación de dependencia + hook + secretos sigue siendo una de las rutas más rentables para un atacante.
Tabla resumen
| Pregunta corta | Respuesta corta |
|---|---|
| ¿Cuál es el punto ciego? | Tratar como dato un archivo que también ejecuta lógica. |
| ¿Dónde pega más? | En instalación, build, test y CI/CD. |
| ¿Qué archivo revisar primero? | Los que usan JS o TS como config y los scripts de package.json. |
| ¿Qué señal es más sospechosa? | Hooks automáticos y imports de dependencias poco claras. |
| ¿Qué control ayuda más? | Reducir lógica en configs y limitar permisos en CI. |
| ¿Qué gana plataforma? | Menos superficie de ataque y cambios más fáciles de auditar. |
La idea no es demonizar la configuración ejecutable. A veces es la forma correcta de resolver un problema complejo. Pero si no la miras como código sensible, dejas una puerta abierta justo donde menos revisas.
Preguntas frecuentes
¿Qué es un config file que ejecuta código?
¿Por qué esto es un riesgo de supply chain?
¿Qué archivos debo revisar primero en un repo Node.js?
¿Se puede evitar por completo el uso de configs ejecutables?
¿Qué control técnico da más resultado rápido?
¿Esto afecta solo a JavaScript y Node.js?
¿Cómo explico este riesgo a un equipo que solo ve configs?
Azirgo
¿Listo para construir tu Producto Digital?
Sitios web, apps móviles, software a medida y soluciones blockchain. Cuéntanos qué tienes en mente y armamos un plan claro contigo.
- Cotización clara en 48 horas
- Equipo en Ecuador, atención en español
- Desde un MVP hasta un producto en producción