High severityNVD Advisory· Published Feb 6, 2026· Updated Feb 9, 2026
Unauthenticated Spree Commerce users can view completed guest orders by Order ID
CVE-2026-25757
Description
Spree is an open source e-commerce solution built with Ruby on Rails. Prior to versions 5.0.8, 5.1.10, 5.2.7, and 5.3.2, unauthenticated users can view completed guest orders by Order ID. This issue may lead to disclosure of PII of guest users (including names, addresses and phone numbers). This issue has been patched in versions 5.0.8, 5.1.10, 5.2.7, and 5.3.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
spree_storefrontRubyGems | < 5.0.8 | 5.0.8 |
spree_storefrontRubyGems | >= 5.1.0, < 5.1.10 | 5.1.10 |
spree_storefrontRubyGems | >= 5.2.0, < 5.2.7 | 5.2.7 |
spree_storefrontRubyGems | >= 5.3.0, < 5.3.2 | 5.3.2 |
Affected products
1Patches
44 files changed · +27 −12
storefront/app/controllers/spree/orders_controller.rb+9 −5 modified@@ -10,11 +10,17 @@ class OrdersController < StoreController before_action :assign_order_with_lock, only: :update + rescue_from CanCan::AccessDenied do |exception| + raise ActiveRecord::RecordNotFound + end + # GET /orders/:id def show @order = complete_order_finder.new(number: params[:id], token: params[:token], store: current_store).execute.first - raise ActiveRecord::RecordNotFound if @order.blank? || !authorize_access + raise ActiveRecord::RecordNotFound if @order.blank? + + authorize! :show, @order, params[:token] @shipments = @order.shipments.includes(:stock_location, :address, selected_shipping_rate: :shipping_method, inventory_units: :line_item) end @@ -49,9 +55,7 @@ def edit private def authorize_access - return true if @order.user_id.nil? - - @order.user == try_spree_current_user + authorize! :show, @order, params[:token] end def find_order_by_cookie @@ -65,7 +69,7 @@ def find_order_by_cookie end def accurate_title - if action_name == 'edit' || action_name == 'update' + if ['edit', 'update'].include?(action_name) Spree.t(:shopping_cart) else Spree.t(:order_number, number: @order&.number)
storefront/app/controllers/spree/order_status_controller.rb+1 −1 modified@@ -7,7 +7,7 @@ def new; end # validate email/order number and redirect to order page # POST /order_status def create - raise ActiveRecord::RecordNotFound if params[:number].blank? + raise ActiveRecord::RecordNotFound if params[:number].blank? || params[:email].blank? @order = order_finder.new(number: params[:number], email: params[:email], store: current_store).execute.first
storefront/spec/controllers/spree/orders_controller_spec.rb+9 −6 modified@@ -105,7 +105,8 @@ end describe '#show' do - let(:order) { create(:completed_order_with_totals, store: store, user: user) } + let(:order) { create(:completed_order_with_totals, store: store) } + let(:user) { nil } it 'renders the show template' do get :show, params: { id: order.number, token: order.token } @@ -120,13 +121,15 @@ end end - context 'when order belongs to another user' do - let(:order) { create(:completed_order_with_totals, store: store, user: create(:user)) } + context 'when token is invalid' do + it 'raises ActiveRecord::RecordNotFound' do + expect { get :show, params: { id: order.number, token: 'invalid' } }.to raise_error(ActiveRecord::RecordNotFound) + end + end + context 'when token is missing' do it 'raises ActiveRecord::RecordNotFound' do - expect do - get :show, params: { id: order.number, token: order.token } - end.to raise_error(ActiveRecord::RecordNotFound) + expect { get :show, params: { id: order.number, token: '' } }.to raise_error(ActiveRecord::RecordNotFound) end end end
storefront/spec/controllers/spree/order_status_controller_spec.rb+8 −0 modified@@ -64,5 +64,13 @@ }.to raise_error(ActiveRecord::RecordNotFound) end end + + context 'with blank email' do + it 'raises ActiveRecord::RecordNotFound' do + expect { + post :create, params: { number: order.number, email: '' } + }.to raise_error(ActiveRecord::RecordNotFound) + end + end end end
4 files changed · +27 −12
storefront/app/controllers/spree/orders_controller.rb+9 −5 modified@@ -10,11 +10,17 @@ class OrdersController < StoreController before_action :assign_order_with_lock, only: :update + rescue_from CanCan::AccessDenied do |exception| + raise ActiveRecord::RecordNotFound + end + # GET /orders/:id def show @order = complete_order_finder.new(number: params[:id], token: params[:token], store: current_store).execute.first - raise ActiveRecord::RecordNotFound if @order.blank? || !authorize_access + raise ActiveRecord::RecordNotFound if @order.blank? + + authorize! :show, @order, params[:token] @shipments = @order.shipments.includes(:stock_location, :address, selected_shipping_rate: :shipping_method, inventory_units: :line_item) end @@ -49,9 +55,7 @@ def edit private def authorize_access - return true if @order.user_id.nil? - - @order.user == try_spree_current_user + authorize! :show, @order, params[:token] end def find_order_by_cookie @@ -65,7 +69,7 @@ def find_order_by_cookie end def accurate_title - if action_name == 'edit' || action_name == 'update' + if ['edit', 'update'].include?(action_name) Spree.t(:shopping_cart) else Spree.t(:order_number, number: @order&.number)
storefront/app/controllers/spree/order_status_controller.rb+1 −1 modified@@ -7,7 +7,7 @@ def new; end # validate email/order number and redirect to order page # POST /order_status def create - raise ActiveRecord::RecordNotFound if params[:number].blank? + raise ActiveRecord::RecordNotFound if params[:number].blank? || params[:email].blank? @order = order_finder.new(number: params[:number], email: params[:email], store: current_store).execute.first
storefront/spec/controllers/spree/orders_controller_spec.rb+9 −6 modified@@ -105,7 +105,8 @@ end describe '#show' do - let(:order) { create(:completed_order_with_totals, store: store, user: user) } + let(:order) { create(:completed_order_with_totals, store: store) } + let(:user) { nil } it 'renders the show template' do get :show, params: { id: order.number, token: order.token } @@ -144,13 +145,15 @@ end end - context 'when order belongs to another user' do - let(:order) { create(:completed_order_with_totals, store: store, user: create(:user)) } + context 'when token is invalid' do + it 'raises ActiveRecord::RecordNotFound' do + expect { get :show, params: { id: order.number, token: 'invalid' } }.to raise_error(ActiveRecord::RecordNotFound) + end + end + context 'when token is missing' do it 'raises ActiveRecord::RecordNotFound' do - expect do - get :show, params: { id: order.number, token: order.token } - end.to raise_error(ActiveRecord::RecordNotFound) + expect { get :show, params: { id: order.number, token: '' } }.to raise_error(ActiveRecord::RecordNotFound) end end end
storefront/spec/controllers/spree/order_status_controller_spec.rb+8 −0 modified@@ -64,5 +64,13 @@ }.to raise_error(ActiveRecord::RecordNotFound) end end + + context 'with blank email' do + it 'raises ActiveRecord::RecordNotFound' do + expect { + post :create, params: { number: order.number, email: '' } + }.to raise_error(ActiveRecord::RecordNotFound) + end + end end end
4 files changed · +27 −12
storefront/app/controllers/spree/orders_controller.rb+9 −5 modified@@ -10,11 +10,17 @@ class OrdersController < StoreController before_action :assign_order_with_lock, only: :update + rescue_from CanCan::AccessDenied do |exception| + raise ActiveRecord::RecordNotFound + end + # GET /orders/:id def show @order = complete_order_finder.new(number: params[:id], token: params[:token], store: current_store).execute.first - raise ActiveRecord::RecordNotFound if @order.blank? || !authorize_access + raise ActiveRecord::RecordNotFound if @order.blank? + + authorize! :show, @order, params[:token] @shipments = @order.shipments.includes(:stock_location, :address, selected_shipping_rate: :shipping_method, inventory_units: :line_item) end @@ -49,9 +55,7 @@ def edit private def authorize_access - return true if @order.user_id.nil? - - @order.user == try_spree_current_user + authorize! :show, @order, params[:token] end def find_order_by_cookie @@ -65,7 +69,7 @@ def find_order_by_cookie end def accurate_title - if action_name == 'edit' || action_name == 'update' + if ['edit', 'update'].include?(action_name) Spree.t(:shopping_cart) else Spree.t(:order_number, number: @order&.number)
storefront/app/controllers/spree/order_status_controller.rb+1 −1 modified@@ -7,7 +7,7 @@ def new; end # validate email/order number and redirect to order page # POST /order_status def create - raise ActiveRecord::RecordNotFound if params[:number].blank? + raise ActiveRecord::RecordNotFound if params[:number].blank? || params[:email].blank? @order = order_finder.new(number: params[:number], email: params[:email], store: current_store).execute.first
storefront/spec/controllers/spree/orders_controller_spec.rb+9 −6 modified@@ -105,7 +105,8 @@ end describe '#show' do - let(:order) { create(:completed_order_with_totals, store: store, user: user) } + let(:order) { create(:completed_order_with_totals, store: store) } + let(:user) { nil } it 'renders the show template' do get :show, params: { id: order.number, token: order.token } @@ -144,13 +145,15 @@ end end - context 'when order belongs to another user' do - let(:order) { create(:completed_order_with_totals, store: store, user: create(:user)) } + context 'when token is invalid' do + it 'raises ActiveRecord::RecordNotFound' do + expect { get :show, params: { id: order.number, token: 'invalid' } }.to raise_error(ActiveRecord::RecordNotFound) + end + end + context 'when token is missing' do it 'raises ActiveRecord::RecordNotFound' do - expect do - get :show, params: { id: order.number, token: order.token } - end.to raise_error(ActiveRecord::RecordNotFound) + expect { get :show, params: { id: order.number, token: '' } }.to raise_error(ActiveRecord::RecordNotFound) end end end
storefront/spec/controllers/spree/order_status_controller_spec.rb+8 −0 modified@@ -64,5 +64,13 @@ }.to raise_error(ActiveRecord::RecordNotFound) end end + + context 'with blank email' do + it 'raises ActiveRecord::RecordNotFound' do + expect { + post :create, params: { number: order.number, email: '' } + }.to raise_error(ActiveRecord::RecordNotFound) + end + end end end
4 files changed · +27 −12
storefront/app/controllers/spree/orders_controller.rb+9 −5 modified@@ -10,11 +10,17 @@ class OrdersController < StoreController before_action :assign_order_with_lock, only: :update + rescue_from CanCan::AccessDenied do |exception| + raise ActiveRecord::RecordNotFound + end + # GET /orders/:id def show @order = complete_order_finder.new(number: params[:id], token: params[:token], store: current_store).execute.first - raise ActiveRecord::RecordNotFound if @order.blank? || !authorize_access + raise ActiveRecord::RecordNotFound if @order.blank? + + authorize! :show, @order, params[:token] @shipments = @order.shipments.includes(:stock_location, :address, selected_shipping_rate: :shipping_method, inventory_units: :line_item) end @@ -49,9 +55,7 @@ def edit private def authorize_access - return true if @order.user_id.nil? - - @order.user == try_spree_current_user + authorize! :show, @order, params[:token] end def find_order_by_cookie @@ -65,7 +69,7 @@ def find_order_by_cookie end def accurate_title - if action_name == 'edit' || action_name == 'update' + if ['edit', 'update'].include?(action_name) Spree.t(:shopping_cart) else Spree.t(:order_number, number: @order&.number)
storefront/app/controllers/spree/order_status_controller.rb+1 −1 modified@@ -7,7 +7,7 @@ def new; end # validate email/order number and redirect to order page # POST /order_status def create - raise ActiveRecord::RecordNotFound if params[:number].blank? + raise ActiveRecord::RecordNotFound if params[:number].blank? || params[:email].blank? @order = order_finder.new(number: params[:number], email: params[:email], store: current_store).execute.first
storefront/spec/controllers/spree/orders_controller_spec.rb+9 −6 modified@@ -105,7 +105,8 @@ end describe '#show' do - let(:order) { create(:completed_order_with_totals, store: store, user: user) } + let(:order) { create(:completed_order_with_totals, store: store) } + let(:user) { nil } it 'renders the show template' do get :show, params: { id: order.number, token: order.token } @@ -144,13 +145,15 @@ end end - context 'when order belongs to another user' do - let(:order) { create(:completed_order_with_totals, store: store, user: create(:user)) } + context 'when token is invalid' do + it 'raises ActiveRecord::RecordNotFound' do + expect { get :show, params: { id: order.number, token: 'invalid' } }.to raise_error(ActiveRecord::RecordNotFound) + end + end + context 'when token is missing' do it 'raises ActiveRecord::RecordNotFound' do - expect do - get :show, params: { id: order.number, token: order.token } - end.to raise_error(ActiveRecord::RecordNotFound) + expect { get :show, params: { id: order.number, token: '' } }.to raise_error(ActiveRecord::RecordNotFound) end end end
storefront/spec/controllers/spree/order_status_controller_spec.rb+8 −0 modified@@ -64,5 +64,13 @@ }.to raise_error(ActiveRecord::RecordNotFound) end end + + context 'with blank email' do + it 'raises ActiveRecord::RecordNotFound' do + expect { + post :create, params: { number: order.number, email: '' } + }.to raise_error(ActiveRecord::RecordNotFound) + end + 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
11- github.com/advisories/GHSA-p6pv-q7rc-g4h9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-25757ghsaADVISORY
- github.com/rubysec/ruby-advisory-db/blob/master/gems/spree_storefront/CVE-2026-25757.ymlghsaWEB
- github.com/spree/spree/blob/1341623f2ae92685cdbe232885bf5808fc8f9ca8/storefront/app/controllers/spree/orders_controller.rbghsax_refsource_MISCWEB
- github.com/spree/spree/blob/1341623f2ae92685cdbe232885bf5808fc8f9ca8/storefront/app/controllers/spree/orders_controller.rbghsax_refsource_MISCWEB
- github.com/spree/spree/blob/a878eb4a782ce0445d218ea86fb12075b0e3d7cc/core/lib/spree/core/number_generator.rbghsax_refsource_MISCWEB
- github.com/spree/spree/commit/3e00be64c128ef4bd4b99731f0c3ab469509cfabghsax_refsource_MISCWEB
- github.com/spree/spree/commit/6b32ed7d474aa55fa441990e6aa39740152aa1beghsax_refsource_MISCWEB
- github.com/spree/spree/commit/6f6b8a7a28a8bff24a6e20eab04b4bbbdf39384dghsax_refsource_MISCWEB
- github.com/spree/spree/commit/ea4a5db590ca753dbc986f2a4e818d9e0edfb1adghsax_refsource_MISCWEB
- github.com/spree/spree/security/advisories/GHSA-p6pv-q7rc-g4h9ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.