On 29 July 2026 the Rails team published GHSA-xr9x-r78c-5hrm, tracked as CVE-2026-66066, a critical flaw in activestorage variant processing (CVSS v4 9.5). In the default configuration, an unauthenticated attacker who can upload a crafted image can cause a variant to be generated and read arbitrary files from the server, including the process environment.
The environment is where Rails apps keep secret_key_base and credentials for external services. With those, file read can become remote code execution or lateral movement. The advisory says technical details of the attack chain are withheld and scheduled for release by 28 August 2026.
The root cause is a bad default: Active Storage did not disable libvips's "unfuzzed" operations, which are unsafe for untrusted content (CWE-1188).
What's affected
- Active Storage below 7.2.3.2.
- 8.0.0 up to 8.0.5.1.
- 8.1.0 up to 8.1.3.1.
Fixed: 7.2.3.2, 8.0.5.1 and 8.1.3.1.
Who is exposed, per the advisory: apps using libvips for Active Storage (variant_processor = :vips, the default since load_defaults 7.0) that accept image uploads from untrusted users. Generating variants is not a separate requirement.
How to check if you're exposed
bundle list | grep -E "^ \* (rails|activestorage) "
vips --version
bundle list | grep ruby-vips
grep -rn "variant_processor" config
grep -rn "has_one_attached\|has_many_attached" app
Reading the output:
- No
has_one_attachedorhas_many_attachedmeans Active Storage is not handling uploads, and the bug is not reachable. - Attachments exist, but only authenticated staff can upload: exposure is smaller, but not zero.
variant_processor = :mini_magickset explicitly: the libvips path is not used.- Public upload form plus libvips: this is the case the advisory describes.
Also check logs for variant requests on unusual file types, and for outbound activity from the app that does not fit its normal pattern.
What to do now
- Upgrade to 7.2.3.2, 8.0.5.1 or 8.1.3.1, then redeploy.
- Upgrade libvips to 8.13 or later. If you are below 8.13 there is no workaround short of removing the libvips dependency, and Rails raises at boot in that case.
- If you cannot upgrade immediately and libvips is 8.13 or later, set the
VIPS_BLOCK_UNTRUSTEDenvironment variable, or callVips.block_untrusted(true)in an initializer with ruby-vips 2.2.1 or later. - Rotate secrets that the process could read:
secret_key_base, the master key and credentials, storage keys (S3, GCS, Azure), database credentials and third party API tokens. The advisory is explicit that upgrading does not undo an exposure that already happened. - Expect rotating
secret_key_baseto log users out and invalidate signed and encrypted data. Plan the cutover.
How this could have been avoided
- Keep secrets out of the process environment where you can. The advisory names the environment as the prize. Short lived credentials from a metadata service or a secrets manager leave less to read.
- Process uploads in a separate, low privilege worker. Variant generation does not need to run in the same container as your web process with the same environment and keys.
- Block untrusted loaders by default. The fix is libvips's own untrusted blocking. Setting it in your own initializer before the patch would have closed the hole.
- Restrict who can upload. A public image form is the exposure condition. Requiring login for uploads shrinks the attacker pool.
- Alert on secret use from unexpected places. Cloud key use from an unfamiliar IP is how a leaked key shows up.
Why it matters
This one is a good reminder that "the default" is a dependency choice. Nothing in the app had to be wrong for exposure: image uploads plus a default processor was enough.
It is also a reachability story in reverse. Instead of asking whether a vulnerable function is called, ask whether the dangerous feature sits on a path that untrusted input reaches. An upload form that generates variants is exactly that.
And secrets rotation is part of the fix, not an optional extra. Patch first, then assume anything the process could read needs replacing if you were exposed.
Try it yourself
Pick one Rails app. Answer four questions: is Active Storage in use, is the processor libvips, can an anonymous user upload, and which secrets live in the process environment. Then list what you would rotate.
Click through it at your own pace, or open it full screen.
Quick check
Why is upgrading Rails not enough if the app was exposed?
The flaw could already have exposed secrets such as secret_key_base and cloud credentials. Those must be rotated, because the patch does not make a leaked secret safe again.
What is the workaround on libvips 8.13 or later if you cannot upgrade yet?
Set the VIPS_BLOCK_UNTRUSTED environment variable, or call Vips.block_untrusted(true) from an initializer with ruby-vips 2.2.1 or later.