ReDos vulnerability on guest checkout email validation
Description
Solidus is a free, open-source ecommerce platform built on Rails. Versions of Solidus prior to 3.1.4, 3.0.4, and 2.11.13 have a denial of service vulnerability that could be exploited during a guest checkout. The regular expression used to validate a guest order's email was subject to exponential backtracking through a fragment like a.a. Versions 3.1.4, 3.0.4, and 2.11.13 have been patched to use a different regular expression. The maintainers added a check for email addresses that are no longer valid that will print information about any affected orders that exist. If a prompt upgrade is not an option, a workaround is available. It is possible to edit the file config/application.rb manually (with code provided by the maintainers in the GitHub Security Advisory) to check email validity.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
solidus_coreRubyGems | < 2.11.13 | 2.11.13 |
solidus_coreRubyGems | >= 3.0.0, < 3.0.4 | 3.0.4 |
solidus_coreRubyGems | >= 3.1.0, < 3.1.4 | 3.1.4 |
Affected products
1Patches
26be174c955faMerge pull request from GHSA-qxmr-qxh6-2cc9
4 files changed · +61 −8
core/lib/spree/core/validators/email.rb+1 −1 modified@@ -12,7 +12,7 @@ module Spree # end # class EmailValidator < ActiveModel::EachValidator - EMAIL_REGEXP = /\A([^@\.]|[^@\.]([^@\s]*)[^@\.])@([^@\s]+\.)+[^@\s]+\z/ + EMAIL_REGEXP = URI::MailTo::EMAIL_REGEXP def validate_each(record, attribute, value) unless EMAIL_REGEXP.match? value
core/lib/tasks/solidus/check_orders_with_invalid_email.rake+18 −0 added@@ -0,0 +1,18 @@ +# frozen_string_literal: true + +namespace :solidus do + desc 'Prints orders with invalid email (after fix for GHSA-qxmr-qxh6-2cc9)' + task check_orders_with_invalid_email: :environment do + matches = Spree::Order.find_each.reduce([]) do |matches, order| + order.email.nil? || Spree::EmailValidator::EMAIL_REGEXP.match?(order.email) ? matches : matches + [order] + end + if matches.any? + puts 'Email / ID / Number' + puts(matches.map do |order| + "#{order.email} / #{order.id} / #{order.number}" + end.join("\n")) + else + puts 'NO MATCHES' + end + end +end
core/spec/lib/spree/core/validators/email_spec.rb+4 −7 modified@@ -24,30 +24,27 @@ class Tester let(:invalid_emails) { [ 'invalid email@email.com', - '.invalid.email@email.com', - 'invalid.email.@email.com', '@email.com', - '.@email.com', 'invalidemailemail.com', '@invalid.email@email.com', 'invalid@email@email.com', 'invalid.email@@email.com' ] } - it 'validates valid email addresses' do + it 'validates valid email addresses', :aggregate_failures do tester = Tester.new valid_emails.each do |email| tester.email_address = email - expect(tester.valid?).to be true + expect(tester.valid?).to be(true), "expected #{email} to be valid" end end - it 'validates invalid email addresses' do + it 'validates invalid email addresses', :aggregate_failures do tester = Tester.new invalid_emails.each do |email| tester.email_address = email - expect(tester.valid?).to be false + expect(tester.valid?).to be(false), "expected #{email} not to be valid" end end end
core/spec/lib/tasks/solidus/check_orders_with_invalid_email_spec.rb+38 −0 added@@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'rails_helper' + +path = Spree::Core::Engine.root.join('lib/tasks/solidus/check_orders_with_invalid_email.rake') + +RSpec.describe 'solidus' do + describe 'check_orders_with_invalid_email' do + include_context( + 'rake', + task_path: path, + task_name: 'solidus:check_orders_with_invalid_email' + ) + + it 'includes orders with invalid email' do + order = create(:order) + order.update_column(:email, 'invalid email@email.com') + + expect { task.invoke }.to output(/invalid email@email.com \/ #{order.id} \/ #{order.number}\n/).to_stdout + end + + it "doesn't include orders with valid email" do + order = create(:order, email: 'valid@email.com') + + expect { task.invoke }.not_to output(/valid@email.com/).to_stdout + end + + it "doesn't include orders with no email" do + order = create(:order, user: nil, email: nil, number: '123') + + expect { task.invoke }.not_to output(/#{order.number}/).to_stdout + end + + it "prints message when no matches found" do + expect { task.invoke }.to output(/NO MATCHES/).to_stdout + end + end +end
9867153e01e3Merge pull request from GHSA-qxmr-qxh6-2cc9
4 files changed · +61 −8
core/lib/spree/core/validators/email.rb+1 −1 modified@@ -12,7 +12,7 @@ module Spree # end # class EmailValidator < ActiveModel::EachValidator - EMAIL_REGEXP = /\A([^@\.]|[^@\.]([^@\s]*)[^@\.])@([^@\s]+\.)+[^@\s]+\z/ + EMAIL_REGEXP = URI::MailTo::EMAIL_REGEXP def validate_each(record, attribute, value) unless EMAIL_REGEXP.match? value
core/lib/tasks/solidus/check_orders_with_invalid_email.rake+18 −0 added@@ -0,0 +1,18 @@ +# frozen_string_literal: true + +namespace :solidus do + desc 'Prints orders with invalid email (after fix for GHSA-qxmr-qxh6-2cc9)' + task check_orders_with_invalid_email: :environment do + matches = Spree::Order.find_each.reduce([]) do |matches, order| + order.email.nil? || Spree::EmailValidator::EMAIL_REGEXP.match?(order.email) ? matches : matches + [order] + end + if matches.any? + puts 'Email / ID / Number' + puts(matches.map do |order| + "#{order.email} / #{order.id} / #{order.number}" + end.join("\n")) + else + puts 'NO MATCHES' + end + end +end
core/spec/lib/spree/core/validators/email_spec.rb+4 −7 modified@@ -24,30 +24,27 @@ class Tester let(:invalid_emails) { [ 'invalid email@email.com', - '.invalid.email@email.com', - 'invalid.email.@email.com', '@email.com', - '.@email.com', 'invalidemailemail.com', '@invalid.email@email.com', 'invalid@email@email.com', 'invalid.email@@email.com' ] } - it 'validates valid email addresses' do + it 'validates valid email addresses', :aggregate_failures do tester = Tester.new valid_emails.each do |email| tester.email_address = email - expect(tester.valid?).to be true + expect(tester.valid?).to be(true), "expected #{email} to be valid" end end - it 'validates invalid email addresses' do + it 'validates invalid email addresses', :aggregate_failures do tester = Tester.new invalid_emails.each do |email| tester.email_address = email - expect(tester.valid?).to be false + expect(tester.valid?).to be(false), "expected #{email} not to be valid" end end end
core/spec/lib/tasks/solidus/check_orders_with_invalid_email_spec.rb+38 −0 added@@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'rails_helper' + +path = Spree::Core::Engine.root.join('lib/tasks/solidus/check_orders_with_invalid_email.rake') + +RSpec.describe 'solidus' do + describe 'check_orders_with_invalid_email' do + include_context( + 'rake', + task_path: path, + task_name: 'solidus:check_orders_with_invalid_email' + ) + + it 'includes orders with invalid email' do + order = create(:order) + order.update_column(:email, 'invalid email@email.com') + + expect { task.invoke }.to output(/invalid email@email.com \/ #{order.id} \/ #{order.number}\n/).to_stdout + end + + it "doesn't include orders with valid email" do + order = create(:order, email: 'valid@email.com') + + expect { task.invoke }.not_to output(/valid@email.com/).to_stdout + end + + it "doesn't include orders with no email" do + order = create(:order, user: nil, email: nil, number: '123') + + expect { task.invoke }.not_to output(/#{order.number}/).to_stdout + end + + it "prints message when no matches found" do + expect { task.invoke }.to output(/NO MATCHES/).to_stdout + end + end +end
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-qxmr-qxh6-2cc9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-43805ghsaADVISORY
- github.com/rubysec/ruby-advisory-db/blob/master/gems/solidus_core/CVE-2021-43805.ymlghsaWEB
- github.com/solidusio/solidus/commit/6be174c955fad84017ca67589c676526bc5ade71ghsaWEB
- github.com/solidusio/solidus/commit/9867153e01e3c3b898cdbcedd7b43375ea922401ghsax_refsource_MISCWEB
- github.com/solidusio/solidus/security/advisories/GHSA-qxmr-qxh6-2cc9ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.