From 284977687c5e3ba764e5228106db0971742ff85c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 19 Mar 2026 10:00:09 +0530 Subject: [PATCH] fix: patch Devise confirmable race condition vulnerability (#13843) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devise 4.9.x has a race condition in the reconfirmable flow where concurrent email change requests can desynchronize the confirmation token from `unconfirmed_email`, letting an attacker confirm an email they don't own. We use `:confirmable` with `reconfirmable = true`, so we're directly exposed. The upstream fix is in Devise 5.0.3, but we can't upgrade — `devise-two-factor` only supports Devise 5 from v6.4.0, which also raised its Rails minimum to 7.2+. No released version supports both Devise 5 and Rails 7.1. This PR ports the Devise 5.0.3 fix locally by overriding `postpone_email_change_until_confirmation_and_regenerate_confirmation_token` on the User model to persist the record before regenerating the token. This is a stopgap — remove it once the dependency chain allows upgrading to Devise 5. ### How to test Sign in as a confirmed user and change your email. The app should send a confirmation to the new address while keeping the current email unchanged until confirmed. --- .bundler-audit.yml | 1 + app/models/user.rb | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.bundler-audit.yml b/.bundler-audit.yml index afe8702ac..7cb453c01 100644 --- a/.bundler-audit.yml +++ b/.bundler-audit.yml @@ -1,3 +1,4 @@ --- ignore: - CVE-2021-41098 # https://github.com/chatwoot/chatwoot/issues/3097 (update once azure blob storage is updated) + - GHSA-57hq-95w6-v4fc # Devise confirmable race condition — patched locally in User model (remove once on Devise 5+) diff --git a/app/models/user.rb b/app/models/user.rb index b14bcd158..443df1ef6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -192,6 +192,25 @@ class User < ApplicationRecord Chatwoot.mfa_enabled? end + # Workaround for Devise 4.9.x race condition vulnerability (GHSA-57hq-95w6-v4fc). + # + # The Confirmable module's reconfirmable flow has a race condition where concurrent + # email change requests can desynchronize confirmation tokens, allowing an attacker + # to confirm an email they don't own. Fixed in Devise 5.0.3 by persisting + # unconfirmed_email before regenerating the confirmation token. + # + # We can't upgrade to Devise 5.0.3 because devise-two-factor only added Devise 5 + # support in v6.4.0, which simultaneously raised its Rails minimum to 7.2+. + # No released version supports both Devise 5 and Rails 7.1. + # + # This override applies the same fix locally: force-mark unconfirmed_email + # as dirty before assignment so ActiveRecord always writes it, keeping it + # in sync with the regenerated confirmation token. Remove once on Devise 5+. + def postpone_email_change_until_confirmation_and_regenerate_confirmation_token + unconfirmed_email_will_change! + super + end + private def remove_macros