
Skill de agente que audita un código base Rails en busca de CVE-2026-66066 (KindaRails2Shell) — lectura arbitraria de archivos / RCE en Active Storage + libvips, comprobando las versiones de Rails y libvips y las mitigaciones block-untrusted.
Una Agent Skill que audita repositorios Ruby on Rails para detectar exposición a CVE-2026-66066 (KindaRails2Shell) — la vulnerabilidad de procesamiento de variantes libvips de Active Storage que permite la lectura arbitraria de archivos y, en una aplicación Rails, la ejecución remota de código.
La skill es un auditor de configuración y una ayuda para la remediación. No contiene código de explotación y no describe la cadena de ataque.
| CVE | CVE-2026-66066 |
| Gravedad | Crítica, CVSS 9.5 |
| Paquete | activestorage (RubyGems) |
| Afectadas | < 7.2.3.2, >= 8.0 < 8.0.5.1, >= 8.1 < 8.1.3.1 |
| Parcheadas | 7.2.3.2, 8.0.5.1, 8.1.3.1 |
| Aviso | GHSA-xr9x-r78c-5hrm |
libvips lee y escribe formatos a través de operaciones, algunas de las cuales marca como unfuzzed — inseguras para contenido no confiable. Active Storage no las deshabilitaba, por lo que un atacante que pueda subir un archivo especialmente diseñado puede alcanzarlas.
Una aplicación está expuesta solo cuando se cumplen las cuatro condiciones siguientes:
activestorage está en un rango de versiones afectadasconfig.active_storage.variant_processor se resuelve a :vipsLa condición 3 es la determinante. El rango afectado < 7.2.3.2 incluye todas las versiones de Rails 6.x, pero 6.x usa :mini_magick por defecto, por lo que 6.x solo está expuesto con una configuración no predeterminada, y las versiones anteriores a 6.0 no tienen ningún ajuste variant_processor en absoluto. Rails 7.0 y posteriores usan :vips por defecto mediante config.load_defaults 7.0, que es la razón por la que la configuración habitual está expuesta. Rails 8.1 también acepta :disabled, lo que hace que la condición 3 sea falsa.
Como plugin de Claude Code, a través del marketplace incluido en este repositorio:
/plugin marketplace add paveg/rails-activestorage-vips-audit
/plugin install rails-activestorage-vips-audit@paveg-skills
Con la CLI de skills, que instala la misma skill en Claude Code, Codex, Cursor, Copilot CLI y otros agentes:
npx skills add paveg/rails-activestorage-vips-audit
O manualmente:
git clone https://github.com/paveg/rails-activestorage-vips-audit.git
cp -r rails-activestorage-vips-audit/skills/rails-activestorage-vips-audit ~/.claude/skills/
~/.agents/skills/ funciona como ubicación transversal entre runtimes para Codex, Copilot CLI y Gemini CLI. Nada en la skill depende de dónde esté instalada — no hay rutas absolutas, y collect-evidence.sh resuelve todo en relación con la raíz de la aplicación a la que se apunta.
La skill tiene dos modos, y ambos aceptan múltiples rutas de aplicación.
report <app>... # read-only audit, one verdict per application
fix <app>... # applies remediation on a branch; never commits or pushes unprompted
Instalada como plugin, cada modo es también un comando, por lo que el modo no tiene que escribirse como argumento:
/rails-activestorage-vips-audit:report path/to/app another/app
/rails-activestorage-vips-audit:fix path/to/app
Los comandos son puntos de entrada para ti, no para el agente: están marcados para que Claude nunca los ejecute por su cuenta. Claude sigue invocando la skill por sí mismo cuando una solicitud coincide con ella, por eso fix no puede comenzar sin que un humano lo pida.
report es el modo por defecto. fix requiere primero un veredicto de report para la misma aplicación, porque la remediación correcta depende de él: Rails 6.x, 7.0.x y 7.1.x no tienen un parche de la misma serie, así que allí fix aplica la mitigación provisional e informa de que se requiere una actualización del framework en lugar de intentar una.
La recopilación de evidencia también puede ejecutarse por separado:
skills/rails-activestorage-vips-audit/scripts/collect-evidence.sh path/to/app another/app
El script recopila hechos y, deliberadamente, no contiene lógica de veredicto. Pasa la raíz de una aplicación, no una raíz de monorepo indiferenciada. Para un monorepo, pasa cada directorio Rails desplegable con su propio Gemfile.lock y config/application.rb como argumento separado. El recopilador excluye esas raíces de aplicación anidadas del flujo de evidencia del padre, mientras conserva las raíces de lockfile anidadas que pueden ser engines montados o dependencias de ruta.
vips --version donde se ejecuta la aplicación. Los informes mantienen este punto bajo preparación del runtime y remediación, en lugar de hacer que el veredicto de exposición dependa de ello.VIPS_BLOCK_UNTRUSTED en un Dockerfile no demuestra que el entorno desplegado la establezca. La skill limita esos hallazgos a «interim mitigation present, upgrade still required» y nunca permite que uno de ellos produzca un veredicto limpio.Actualiza a 7.2.3.2, 8.0.5.1 o 8.1.3.1, según tu serie. Confirma primero libvips >= 8.13 y ruby-vips >= 2.2.1 en tiempo de ejecución: donde ruby-vips esté instalado, el Active Storage parcheado lanza una excepción durante el arranque a menos que ambas condiciones se cumplan, y esa comprobación se aplica también a las aplicaciones :mini_magick.
Mitigaciones provisionales, ninguna de las cuales sustituye a la actualización:
VIPS_BLOCK_UNTRUSTED (libvips >= 8.13; no es necesario cambiar ninguna gema)Vips.block_untrusted(true) desde un inicializador (ruby-vips >= 2.2.1, más libvips >= 8.13)Con ruby-vips < 2.2.1, la vía del inicializador llama a un método que aún no existe, así que sube primero la versión de la gema u opta por la vía de la variable de entorno, que no depende de ella.
Si una aplicación estuvo expuesta, trata todos los secretos legibles por el proceso de la aplicación como comprometidos y rótalos: secret_key_base, la clave maestra, las credenciales del servicio de Active Storage, las credenciales de la base de datos y los tokens de terceros.
Un WAF no es una mitigación aquí. Que pueda ver el payload depende del servicio de almacenamiento y del método de subida, por lo que su efectividad depende demasiado de la configuración como para poder confiar en ella.
La vulnerabilidad fue encontrada y reportada de forma independiente por RyotaK de GMO Flatt Security y por un equipo de Ethiack formado por André Baptista, Bruno Mendes y Castilho, y se divulgó en coordinación con los mantenedores de Rails.