CVE-2026-94462: Spree: Broken Access Control in `PATCH /api/v3/store/carts/:id/associate` (IDOR)

Published Sep 22, 2026
·
Updated

Summary

The Store API v3 endpoint PATCH /api/v3/store/carts/:id/associate binds a guest cart to the authenticated caller without verifying possession of that cart. It locates the cart by prefixed ID only — currentstore.carts.where(user: [nil, currentuser]).findbyprefixid!(params[:id]) — and omits the authorize!(:update, @cart, carttoken) check that every other action in the controller performs via CartResolvable. Because prefixed IDs are a reversible Sqids encoding of the auto-increment primary key (obfuscation, not a token), an authenticated customer can name arbitrary guest cart IDs, take them over, and read the checkout addresses stored on them. This is broken access control / IDOR, reachable by any low-privilege registered user.

Severity

Requires an authenticated store account and depends on target guest carts already carrying an address and not yet being associated, on a store not running in loginrequired mode. Confidentiality impact is the driver (guest checkout PII); integrity impact is limited and recoverable (cart reassignment + email overwrite on an in-progress cart). Not Critical: the action is gated behind authentication (PR:L, not PR:N) and constrained by cart state, so it is not anonymously exploitable.

Details

Root cause: associate skips the cart-possession check its sibling actions enforce and trusts a guessable identifier as the sole locator.

Entry point. Spree::Api::V3::Store::CartsController#associate (cartscontroller.rb:88-96), guarded only by prependbeforeaction :requireauthentication!, only: [:index, :associate]. That requires the caller be authenticated; it does not tie the request to a specific guest cart.

ruby spree/api/app/controllers/spree/api/v3/store/cartscontroller.rb:88-96 PATCH /api/v3/store/carts/:id/associate def associate @cart = findcartforassociation

result = Spree.cartassociateservice.call(guestorder: @cart, user: currentuser, guestonly: true)

if result.success? rendercart else renderserviceerror(result.error.tos) end end

Missing check. findcartforassociation (cartscontroller.rb:177-178) resolves any guest cart (user IS NULL) in the store by ID with no authorize!(..., carttoken). Contrast CartResolvable#findcart!, which binds the token.

ruby spree/api/app/controllers/spree/api/v3/store/cartscontroller.rb:177-178 def findcartforassociation currentstore.carts.where(user: [nil, currentuser]).findbyprefixid!(params[:id]) end

Identifier. prefixedid is "cart" + SQIDS.encode([id]) with SQIDS = Sqids.new(minlength: 10) (prefixedid.rb:17,56) — default alphabet, no salt, no blocklist. Sqids is non-cryptographic and reversible, so candidate IDs are derivable offline from sequential primary keys.

ruby spree/core/app/models/concerns/spree/prefixedid.rb:17-56 SQIDS = Sqids.new(minlength: 10)

def prefixedid return nil unless id.present?

"#{self.class.prefixidprefix}#{Spree::PrefixedId::SQIDS.encode([id])}" end

Data flow. Spree.cartassociateservice.call(guestorder: @cart, user: currentuser, guestonly: true) reassigns the owner and overwrites email, preserving existing addresses via billaddress ||= / shipaddress ||=. rendercart then serializes billingaddress/shippingaddress (firstname, lastname, address1, address2, city, postalcode, phone, company) back to the caller.

PoC

Preconditions: attacker holds an ordinary store account (self-service registration) and the store's publishable key (a front-end credential, present in any headless storefront bundle); one or more guest carts carry checkout addresses; store is not in loginrequired mode.

1. Authenticate: POST /api/v3/store/auth/login → attacker JWT. 2. Derive candidate IDs offline: "cart" + Sqids.encode([n]) for a range of n. 3. For each candidate: PATCH /api/v3/store/carts/<id>/associate with the attacker JWT. A hit returns 200 with the victim's billingaddress/shippingaddress; non-guest or missing carts return 404/422.

Impact

Confidentiality: an authenticated attacker can enumerate guest cart IDs and read checkout PII (name, street, postal code, phone) on carts they don't own. Integrity: limited and recoverable — each call reassigns the guest cart and overwrites its email, disrupting the original guest's in-progress cart. Requires a registered account, so not anonymously exploitable.

Remediation

Update to Spree 5.4.4 or 5.5.4. Your storefront, based on https://github.com/spree/storefront, doesn't need any updates because it has always sent a cart token when associating carts; this is a backend issue.

Other sources

Spree is an open source e-commerce solution built with Ruby on Rails. From 5.4.0 until 5.4.4 and 5.5.4, PATCH /api/v3/store/carts/:id/associate in Spree::Api::V3::Store::CartsController#associate uses findcartforassociation to locate a cart by prefixedid but does not require a cart token or otherwise verify possession of the selected guest cart. An authenticated customer can derive reversible prefixed cart IDs, associate an eligible guest cart with the attacker's account, and receive billing and shipping address data from the cart. Exploitation requires a guest cart with address data on a store that does not require login for checkout, and reassignment can also disrupt the guest's in-progress cart. This issue is fixed in versions 5.4.4 and 5.5.4.

MITRE

Affected Software

3 affected componentsFixes available
Spree Spree>=5.4.0<5.4.4, >=5.5.0<5.5.4
rubygems/spree_api>=5.5.0<5.5.4
5.5.4
rubygems/spree_api>=5.4.0<5.4.4
5.4.4

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade rubygems/spree_api to a version that resolves this vulnerability.

    Fixed in 5.5.4
  2. Upgrade

    Upgrade rubygems/spree_api to a version that resolves this vulnerability.

    Fixed in 5.4.4
  3. Upgrade

    Upgrade Spree to a version that resolves this vulnerability.

    Fixed in 5.4.4
  4. Upgrade

    Upgrade Spree to a version that resolves this vulnerability.

    Fixed in 5.5.4

Event History

Sep 22, 2026
CVE Published
via MITRE·06:57 PM
Data Sourced
via MITRE·06:57 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·07:16 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·08:40 PM
Data Sourced
via GitHub·08:40 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments are exposed to this issue?

Spree deployments running versions from 5.4.0 through 5.4.4 and version 5.5.4 are affected. Exploitation additionally depends on the store allowing checkout without login and having guest carts that contain billing or shipping address data.

2

What does an attacker need to exploit it?

The attacker needs an authenticated customer account and an eligible guest cart. They can derive reversible prefixed cart IDs and use the associate endpoint without possessing that guest cart's token.

3

What information and impact can result from exploitation?

An attacker can associate a guest cart with their own account and receive the cart's billing and shipping address data. Reassigning the cart can also disrupt the original guest's in-progress checkout.

4

What is the remediation?

Upgrade to a fixed version, identified as 5.4.4 or 5.5.4. If upgrading is not immediately possible, stores that require login for checkout do not meet the stated exploitation condition.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203