Una comprobación verde no es una barrera de lanzamiento
Un pull request necesita libertad suficiente para compilar y probar código. No debería heredar la autoridad para publicar un paquete. La actualización de GitHub del 28 de julio sobre la cadena de suministro explica por qué: ataques recientes encadenaron un compromiso inicial del workflow con robo de credenciales, contaminación de cachés y versiones maliciosas distribuidas entre varios proyectos.
El diseño peligroso no es solo que "CI tiene un secreto". Es una ruta de confianza. Código sin revisar se ejecuta en un job, deja resultados o estado, y un job posterior con más autoridad trata ese resultado como seguro. Si el job privilegiado tiene un token reutilizable del registro, acceso de escritura al repositorio o una caché mutable compartida, el pull request puede obtener una ruta hacia la autoridad de publicación sin editar el workflow de lanzamiento.
Según comprobamos el 26 de septiembre de 2026, la referencia de uso seguro de GitHub advierte que jobs privilegiados con pull_request_target y workflow_run pueden exponer acceso de escritura, secretos y estado compartido de caché cuando descargan o consumen código no confiable. Un resultado verde solo indica que pasaron las comprobaciones configuradas. No demuestra que el job de prueba estuviera aislado de la ruta de lanzamiento.
La condición de aceptación práctica es más fuerte: el código de un pull request puede fallar pruebas, producir artefactos y pedir revisión, pero no puede crear una credencial de publicación ni influir en lo que libera un job privilegiado sin un paso separado de verificación.
Separa las pruebas no confiables de la publicación privilegiada
Empieza dibujando dos carriles.
El primer carril procesa pull requests. Descarga el commit propuesto, instala dependencias, ejecuta lint, pruebas y una compilación, y sube evidencia. Da a su GITHUB_TOKEN permiso de solo lectura sobre contenidos, salvo que un paso específico necesite más. No expongas entornos de producción ni credenciales del registro. Trata cada archivo, nombre de artefacto, entrada de caché y comando producido por este carril como entrada controlada por un atacante.
El segundo carril publica. Actívalo desde una etiqueta revisada, una rama protegida o un entorno aprobado manualmente. Descarga de nuevo el commit exacto que fue revisado en lugar de reutilizar el espacio de trabajo del job del pull request. Compila la versión desde un grafo de dependencias bloqueado, o verifica el digest de un artefacto antes de aceptarlo. El carril de publicación debe saber exactamente qué commit libera y por qué ese commit pasó a ser elegible.
Esta separación importa incluso si el repositorio es privado. Una cuenta de desarrollador, dependencia o acción de terceros comprometida todavía puede alcanzar un workflow. GitHub señala que una sola acción comprometida puede acceder a los secretos disponibles para su job y usar el token del job para escribir en el repositorio. El acceso privado reduce quién puede abrir un pull request. No convierte la ejecución no confiable en privilegiada por defecto.
Evita descargar código del pull request dentro de pull_request_target. Ese trigger se ejecuta en el contexto del repositorio base. Si lo necesitas para etiquetas o comentarios, mantén el job limitado a metadatos. GitHub ahora aplica valores predeterminados más seguros para el checkout en casos explotados con frecuencia, pero una exclusión explícita puede reabrir la ruta. Revisa juntos el trigger, la referencia descargada y los permisos.
Elimina el secreto reutilizable de publicación
Un token npm de larga duración convierte cualquier robo exitoso en acceso continuo de publicación hasta que alguien lo detecta y revoca. Guardar el token en un almacén de secretos mejor lo protege en reposo, pero el workflow todavía tiene que revelarlo al proceso de publicación durante la ejecución.
La documentación de trusted publishing de npm describe un modelo más limitado. El registro confía en un repositorio y workflow concretos, y luego intercambia una identidad firmada con OpenID Connect por una credencial de publicación de corta duración y específica para ese workflow. No existe un token npm de escritura reutilizable que el job pueda imprimir, copiar o guardar.
Eso reduce de forma significativa el radio de impacto, pero no es una defensa completa. El workflow con id-token: write todavía puede solicitar un token de identidad. Por tanto, su trigger, el commit descargado y las protecciones del entorno siguen formando parte de la barrera de seguridad. Configura id-token: write solo en el job de publicación, no en todo el workflow ni en toda la organización.
Separa también las credenciales de instalación. Si la compilación necesita paquetes privados, usa un token de solo lectura para instalarlos. La publicación debe usar la identidad de corta duración. Un token de lectura robado puede exponer código privado, lo cual es grave, pero no debería permitir además una versión maliciosa.
Después de comprobar que trusted publishing funciona, deshabilita la publicación tradicional mediante tokens y revoca los tokens antiguos de automatización. De lo contrario, la ruta más segura existirá al lado del bypass original.
Añade una segunda barrera de lanzamiento
Las credenciales de corta duración responden quién solicita publicar. No responden si esa versión debería hacerse pública.
npm admite publicación por etapas: CI puede preparar una versión y un mantenedor la aprueba con autenticación de dos factores antes de hacerla pública. Un entorno de GitHub con revisores obligatorios aporta otra barrera antes de que el job de publicación reciba su autoridad. Cualquiera de los dos enfoques rompe la suposición de que una ejecución comprometida del workflow es suficiente.
Elige la barrera según el modelo de lanzamiento. Un paquete interno pequeño puede usar una etiqueta protegida y revisión obligatoria del entorno. Un paquete público con muchos usuarios indirectos puede justificar la publicación por etapas y una revisión explícita del paquete. La propiedad importante es la independencia: el actor o paso que aprueba el lanzamiento no debe repetir simplemente una decisión ya tomada por el carril no confiable.
Revisa el contenido del paquete, no solo el diff del código fuente. Los bundles generados pueden incluir archivos ausentes en la vista del pull request. Ejecuta npm pack --dry-run, inspecciona la lista de archivos y compara la versión y la procedencia del paquete con el commit revisado. Un árbol fuente correcto todavía puede producir el artefacto equivocado por un script de compilación modificado o una regla de archivos ignorados.
Audita la ruta, no solo el YAML
Una revisión del workflow debe seguir la autoridad desde el trigger hasta el registro.
Enumera cada evento que puede iniciar el workflow. Registra qué commit se descarga. Expande workflows reutilizables y acciones de terceros. Fija las acciones de terceros a SHAs completos de commit, que GitHub identifica como la opción inmutable. Comprueba permisos por job, revisores del entorno, acceso a caché, productores de artefactos y destinos de red.
Después prueba los casos negativos. Abre un pull request desde un fork y confirma que no recibe credenciales de publicación. Cambia el nombre de un artefacto y confirma que el job de lanzamiento lo rechaza. Intenta ejecutar el job de publicación desde una rama no protegida. Elimina una aprobación. Un control que solo funciona en la ruta esperada todavía no ha demostrado su límite.
Conserva evidencia suficientemente pequeña para revisarla: el commit de lanzamiento, el digest del artefacto, la ejecución del workflow, la identidad que aprobó y el resultado del registro. Durante un incidente, esto es más útil que una captura de una pipeline verde.
Qué no cambia
Separar workflows no vuelve confiables las dependencias. Una dependencia maliciosa puede ejecutarse durante la compilación privilegiada si no controlas los scripts de instalación, lockfiles y fuentes de paquetes. Los cambios de plataforma de GitHub reducen rutas comunes de ataque, pero no inspeccionan la lógica de tu aplicación ni deciden si una versión es segura.
OIDC tampoco elimina todos los secretos. La instalación de dependencias privadas, los sistemas de firma o las plataformas de despliegue todavía pueden necesitar credenciales. Da a cada una su propio alcance y duración en lugar de reunirlas en un solo job de lanzamiento.
El resultado útil es más limitado: el código de un pull request puede demostrar que compila, pero no puede publicar por sí solo. El job de lanzamiento parte de un commit elegible de forma independiente, recibe autoridad de corta duración y deja evidencia que vincula el artefacto del registro con ese commit. Así, la publicación deja de ser un efecto secundario de CI y se convierte en una decisión de seguridad separada.
Escrito por Dandelion Labs