From 2c13fbfb3ddc302ad84d549ecccaea03bce690f6 Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Sun, 2 Aug 2026 12:28:58 -0500 Subject: [PATCH 1/2] CP-14829 - Patch critical image-upload security vulnerability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What DocuSeal now runs a patched Rails release and blocks unsafe image processing so crafted uploads cannot read server files or secrets. Why CVE-2026-66066 is a critical Active Storage / libvips issue. Submitters can upload images without auth, so production was exposed until patched. How to test 1. Confirm Gemfile.lock has rails/activestorage 8.0.5.1 2. Boot the app and complete a signing flow (signature PNG/JPEG, optional stamp) 3. Confirm template preview and completed result PDF still generate 4. Deploy staging, re-check a full e2e sign → complete, then deploy production --- Dockerfile | 2 + Gemfile | 2 +- Gemfile.lock | 112 ++++++++++---------- config/initializers/vips_block_untrusted.rb | 10 ++ 4 files changed, 70 insertions(+), 56 deletions(-) create mode 100644 config/initializers/vips_block_untrusted.rb diff --git a/Dockerfile b/Dockerfile index cdce1fec06..74533e1e00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,8 @@ ENV RAILS_ENV=production ENV BUNDLE_WITHOUT="development:test" ENV LD_PRELOAD=/lib/libgcompat.so.0 ENV OPENSSL_CONF=/app/openssl_legacy.cnf +# CVE-2026-66066: block unfuzzed libvips loaders process-wide (puma/sidekiq/console) +ENV VIPS_BLOCK_UNTRUSTED=1 WORKDIR /app diff --git a/Gemfile b/Gemfile index 7b3ea703aa..deedc6204d 100644 --- a/Gemfile +++ b/Gemfile @@ -30,7 +30,7 @@ gem 'premailer-rails' gem 'pretender' gem 'puma', require: false gem 'rack' -gem 'rails' +gem 'rails', '~> 8.0.5' gem 'rails_autolink' gem 'rails-i18n' gem 'rotp' diff --git a/Gemfile.lock b/Gemfile.lock index f556a5ddf9..f5514fa9ac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,29 +1,29 @@ GEM remote: https://rubygems.org/ specs: - actioncable (8.0.1) - actionpack (= 8.0.1) - activesupport (= 8.0.1) + actioncable (8.0.5.1) + actionpack (= 8.0.5.1) + activesupport (= 8.0.5.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.1) - actionpack (= 8.0.1) - activejob (= 8.0.1) - activerecord (= 8.0.1) - activestorage (= 8.0.1) - activesupport (= 8.0.1) + actionmailbox (8.0.5.1) + actionpack (= 8.0.5.1) + activejob (= 8.0.5.1) + activerecord (= 8.0.5.1) + activestorage (= 8.0.5.1) + activesupport (= 8.0.5.1) mail (>= 2.8.0) - actionmailer (8.0.1) - actionpack (= 8.0.1) - actionview (= 8.0.1) - activejob (= 8.0.1) - activesupport (= 8.0.1) + actionmailer (8.0.5.1) + actionpack (= 8.0.5.1) + actionview (= 8.0.5.1) + activejob (= 8.0.5.1) + activesupport (= 8.0.5.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.1) - actionview (= 8.0.1) - activesupport (= 8.0.1) + actionpack (8.0.5.1) + actionview (= 8.0.5.1) + activesupport (= 8.0.5.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -31,35 +31,35 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.1) - actionpack (= 8.0.1) - activerecord (= 8.0.1) - activestorage (= 8.0.1) - activesupport (= 8.0.1) + actiontext (8.0.5.1) + actionpack (= 8.0.5.1) + activerecord (= 8.0.5.1) + activestorage (= 8.0.5.1) + activesupport (= 8.0.5.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.1) - activesupport (= 8.0.1) + actionview (8.0.5.1) + activesupport (= 8.0.5.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.1) - activesupport (= 8.0.1) + activejob (8.0.5.1) + activesupport (= 8.0.5.1) globalid (>= 0.3.6) - activemodel (8.0.1) - activesupport (= 8.0.1) - activerecord (8.0.1) - activemodel (= 8.0.1) - activesupport (= 8.0.1) + activemodel (8.0.5.1) + activesupport (= 8.0.5.1) + activerecord (8.0.5.1) + activemodel (= 8.0.5.1) + activesupport (= 8.0.5.1) timeout (>= 0.4.0) - activestorage (8.0.1) - actionpack (= 8.0.1) - activejob (= 8.0.1) - activerecord (= 8.0.1) - activesupport (= 8.0.1) + activestorage (8.0.5.1) + actionpack (= 8.0.5.1) + activejob (= 8.0.5.1) + activerecord (= 8.0.5.1) + activesupport (= 8.0.5.1) marcel (~> 1.0) - activesupport (8.0.1) + activesupport (8.0.5.1) base64 benchmark (>= 0.3) bigdecimal @@ -321,7 +321,7 @@ GEM net-imap net-pop net-smtp - marcel (1.0.4) + marcel (1.2.1) matrix (0.4.2) method_source (1.1.0) mini_magick (4.13.2) @@ -403,20 +403,20 @@ GEM rack (>= 1.3) rackup (2.2.1) rack (>= 3) - rails (8.0.1) - actioncable (= 8.0.1) - actionmailbox (= 8.0.1) - actionmailer (= 8.0.1) - actionpack (= 8.0.1) - actiontext (= 8.0.1) - actionview (= 8.0.1) - activejob (= 8.0.1) - activemodel (= 8.0.1) - activerecord (= 8.0.1) - activestorage (= 8.0.1) - activesupport (= 8.0.1) + rails (8.0.5.1) + actioncable (= 8.0.5.1) + actionmailbox (= 8.0.5.1) + actionmailer (= 8.0.5.1) + actionpack (= 8.0.5.1) + actiontext (= 8.0.5.1) + actionview (= 8.0.5.1) + activejob (= 8.0.5.1) + activemodel (= 8.0.5.1) + activerecord (= 8.0.5.1) + activestorage (= 8.0.5.1) + activesupport (= 8.0.5.1) bundler (>= 1.15.0) - railties (= 8.0.1) + railties (= 8.0.5.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -431,13 +431,14 @@ GEM actionview (> 3.1) activesupport (> 3.1) railties (> 3.1) - railties (8.0.1) - actionpack (= 8.0.1) - activesupport (= 8.0.1) + railties (8.0.5.1) + actionpack (= 8.0.5.1) + activesupport (= 8.0.5.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.2.1) @@ -551,6 +552,7 @@ GEM thor (1.3.2) timeout (0.4.3) trailblazer-option (0.1.2) + tsort (0.2.0) turbo-rails (2.0.11) actionpack (>= 6.0.0) railties (>= 6.0.0) @@ -638,7 +640,7 @@ DEPENDENCIES pry-rails puma rack - rails + rails (~> 8.0.5) rails-i18n rails_autolink rotp diff --git a/config/initializers/vips_block_untrusted.rb b/config/initializers/vips_block_untrusted.rb new file mode 100644 index 0000000000..748a9e28e9 --- /dev/null +++ b/config/initializers/vips_block_untrusted.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# CVE-2026-66066: block libvips loaders unsafe for untrusted input. +# Covers Active Storage and first-party Vips::Image.new_from_buffer call sites. +# Requires libvips >= 8.13 and ruby-vips >= 2.2.1. +if defined?(Vips) && Vips.respond_to?(:block_untrusted) + Vips.block_untrusted(true) +else + warn 'WARNING: Vips.block_untrusted unavailable (libvips < 8.13?) — CVE-2026-66066 mitigation NOT active' +end From b9365f9662490a0e387e0c4ce1ef8161c5fc5b66 Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Sun, 2 Aug 2026 15:47:13 -0500 Subject: [PATCH 2/2] CP-14829 - Add airbrake error when fail --- config/initializers/vips_block_untrusted.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/config/initializers/vips_block_untrusted.rb b/config/initializers/vips_block_untrusted.rb index 748a9e28e9..5bcd70cfd3 100644 --- a/config/initializers/vips_block_untrusted.rb +++ b/config/initializers/vips_block_untrusted.rb @@ -6,5 +6,14 @@ if defined?(Vips) && Vips.respond_to?(:block_untrusted) Vips.block_untrusted(true) else - warn 'WARNING: Vips.block_untrusted unavailable (libvips < 8.13?) — CVE-2026-66066 mitigation NOT active' + # Fail loudly (but do not block boot) so a missing mitigation cannot be missed + # in staging/production. Matches careerplug_webhook_config.rb reporting style. + message = 'Vips.block_untrusted unavailable (libvips < 8.13 or ruby-vips < 2.2.1?) — ' \ + 'CVE-2026-66066 mitigation NOT active' + warn "WARNING: #{message}" + + unless Rails.env.local? + Rails.logger.error("[vips_block_untrusted] #{message}") + Airbrake.notify(message) + end end