Drupal Commerce Access Bypass, Exploited in the Wild (CVE-2026-118539)
Drupal Commerce before 2.41 and 3.3.13 lets limited users take over other vendors' variations and coupons via JSON:API; exploited.
TL;DR
- Drupal Commerce Core does not protect the entity fields that link products, variations, coupons and promotions across ownership boundaries. A user with limited permissions can take over another vendor's product variations or link other promotions' coupons through JSON:API or REST. Customers could also change restricted fields of their own carts, such as state and totals.
- Drupal rates the flaw Highly critical, 21 out of 25 on its own scale, and states it is being actively exploited in the wild. The advisory gives no actors, scale or indicators. CVE-2026-118539 carries no CVSS score as of 2026-10-30.
- Every Commerce Core release before 2.41 and every 3.0.0 to 3.3.12 release is affected. The 2.x line had seen no release since August 2024 before this fix.
- Upgrade to Commerce Core 3.3.13, or 2.41 on the older line, and audit existing orders for inconsistencies as the advisory instructs. See "How do I check if I was compromised?" for the audit angles the public material supports.
Am I affected?
You are exposed when all of these hold:
- The Commerce Core module is installed at a version before 2.41, or between 3.0.0 and 3.3.12 inclusive.
- Accounts other than full store administrators can write to commerce entities: customers updating their own carts, or accounts with commerce permissions such as updating their own products, managing variations or updating their own promotions. The flaw is an access bypass for such accounts.
- JSON:API or REST API access exists, which the tests in the fix use for every takeover path. JSON:API ships with Drupal core.
Customers need no commerce permission: before the fix, a customer with update access to their own cart could change restricted order and order-item fields such as state, total_price, adjustments, unit_price and title through JSON:API or REST, and reach order admin routes for that cart. The vendor takeover paths need an account with limited commerce permissions. Accounts that could not view some variations, such as those of unpublished products, could also learn from JSON:API collection filters that those variations existed.
| Branch | Affected versions | Fixed version |
|---|---|---|
| Commerce Core 3.x | 3.0.0 to 3.3.12 | 3.3.13 (2026-10-28) |
| Commerce Core 2.x and earlier | All versions before 2.41 | 8.x-2.41 (2026-10-28) |
Both fixes shipped on the same day. The 2.x line's previous release, 8.x-2.40, dates from 2024-08-22, so sites on it had gone more than two years without an update.
Check what your site runs:
# Composer-installed sites: the resolved version in the lock file
grep -A1 '"name": "drupal/commerce"' composer.lock | grep version
# Or ask Composer directly
composer show drupal/commerce 2>/dev/null | grep ^versions
# Without Composer: the module's .info.yml carries the version when
# packaged from drupal.org
grep -r "version:" web/modules/contrib/commerce/*.info.yml 2>/dev/null
How do I fix it?
- With Composer, as the standard Drupal workflow:
composer require 'drupal/commerce:^3.3.13' --with-all-dependencies, orcomposer require 'drupal/commerce:^2.41' --with-all-dependencieson the 2.x line. - Run the update pipeline your site uses after code changes, then clear caches:
drush cr. - Re-apply any custom patches against the module, since both branches moved.
- Audit existing orders, as the advisory instructs, using the checks below.
No configuration workaround exists. The advisory names none, and the fix's own tests show the bypass paths close only when the new field-level checks are in place.
What is the root cause?
Commerce entities had access control at the entity level but none at the field level, so reference fields that link one vendor's entities to another's could be edited by any account allowed to touch its own entities.
Commerce Core sells goods and services through products, variations, promotions and coupons, each with its own permissions. An account with "update own commerce_product" can edit its products. The 3.3.13 diff shows that nothing stopped such an account from also editing the reference fields that cross ownership: the variations list on a product, the promotion a coupon belongs to, the coupons a promotion references, and the store flag that changes site-wide behavior.
The fix introduces a restricted_fields property on each entity type and checks it in new access control handlers. This is the variation handler from the 3.3.13 diff:
// modules/product/src/ProductVariationAccessControlHandler.php
$restricted_fields = $this->entityType->get('restricted_fields') ?? [];
if ($operation == 'edit' && in_array($field_definition->getName(), $restricted_fields, TRUE)) {
$result = AccessResult::allowedIfHasPermission($account, $this->entityType->getAdminPermission());
/** @var \Drupal\commerce_product\Entity\ProductVariationInterface|null $variation */
$variation = $items?->getEntity();
if ($result->isAllowed() || !$variation?->isNew()) {
return $result;
}
// Users who can create variations can set these fields on new variations
// (e.g. through JSON:API or REST), as on the variation add form, which
// allows adding variations to any product.
return $result->orIf(AccessResult::allowedIfHasPermission($account, "manage {$variation->bundle()} commerce_product_variation"));
}
return parent::checkFieldAccess($operation, $field_definition, $account, $items);
Restricted fields now include:
- Variations:
product_id. - Coupons:
promotion_id. - Promotions:
coupons. - Stores:
is_default. - Orders and order items: the fields that carry ownership, money and state, such as
uid,store_id,state,order_items,total_price,adjustments,unit_price,titleandpurchased_entity, pluscoupons,cart,checkout_flow,checkout_step,payment_gatewayandpayment_methodon orders. - Payment methods: the gateway, owner, remote ID and card fields.
Editing them requires the admin permission, an update-any or order-type update permission where the handler allows it, or, for new variations, the variation-management permission the add form itself uses.
The same diff closes the delete paths the references opened. Promotion::postDelete() now deletes only the coupons whose promotion matches the deleted entity, with the comment "Coupons belonging to other promotions might be referenced (e.g. through JSON:API), those must not be deleted". The product equivalent deletes only the product's own variations.
Routes also lacked permission checks: every order admin route under /admin/ that only checked order access, including entity.commerce_order.manage_coupons_form, the order form-mode route and the order item routes, now requires "access commerce administration pages", because customers hold update access to their own carts. And coupons had no query access handler at all, so the entity autocomplete route listed every coupon code to any account that could reach it; a new selection plugin restricts that listing to accounts with "administer commerce_promotion" or "update any commerce_promotion".
How is it exploited?
No exploit code is public. The fix's own functional tests document the exact requests that worked before and fail after:
- A user with rights to create their own product sends a JSON:API POST that creates a product referencing another product's variation. Before the fix, the variation moved to the new product's ownership; after it, the request fails with 422 and the message "belongs to another product".
- A promotion owner sends a JSON:API PATCH that moves one of their coupons to another promotion by changing its
promotion_idrelationship. Before the fix, the coupon moved; after it, the request returns 403. - Any account that could reach the entity autocomplete route received coupon codes in the autocomplete results, including codes of promotions the account could not manage.
- JSON:API collection queries matched variations the account could not view, including variations of unpublished products, and listed them as omitted, revealing that a filter matched them.
The flaw being actively exploited is Drupal's statement. No public write-up names the attackers, the requests they sent or the sites they hit, and the advisory gives no indicators.
How does the attack flow?
One sentence: a limited-privileged account edits a reference field through JSON:API or REST, attaching another vendor's variation or another promotion's coupon to an entity it controls, and the platform moves the ownership with it.
commerce permissions"] --> B["JSON:API or REST
write request"] B --> C["Reference field edit:
variations, coupons,
promotion_id"] C --> D["No field-level check:
request accepted"] D --> E["Another vendor's variation
or coupon is linked
to the attacker's entity"] E --> F["Financial impact
on the vendor"] G["Autocomplete route:
coupon codes listed
to any caller"] --> D
What can an attacker do?
With an account that holds limited commerce permissions, an attacker can move another vendor's product variations into an entity they control, move coupons between promotions, and list coupon codes that were never intended for their eyes. Before the fix, deleting a crafted promotion also deleted coupons that belonged to other promotions. Drupal summarizes the impact as an access bypass "that may financially impact the vendor".
The conditions: a customer with a cart, for tampering with that cart's order fields, or an account with some commerce permission for the vendor takeover paths, plus API access. Accounts without view access could also learn that hidden variations exist, per the query-access fix.
The advisory's exploitation statement is the only public evidence of abuse. It names nothing beyond "actively exploited in the wild", so treat any site with affected versions and multi-vendor or multi-role accounts as exposed since an unknown date, and audit as instructed.
Why does the fix work?
The fix adds the missing layer: field-level access control on the exact fields that cross ownership boundaries, enforced by access control handlers that run before the reference is written. The pattern is the same for every entity: the entity type declares its restricted fields, and checkFieldAccess() denies the edit unless the account holds the admin permission or, depending on the entity type, an update-any or order-type update permission.
Three accompanying changes close the remaining paths:
CouponSelection::getReferenceableEntities()returns an empty list for accounts that cannot administer promotions, so the autocomplete route no longer leaks codes. Redeeming a coupon still works, because validation is a different path from listing.Promotion::postDelete()and the product equivalent check ownership before deleting referenced entities.- The order admin routes,
manage_coupons_formamong them, gain their permission requirement.
The fix is complete for the paths its tests cover: variation takeover, coupon movement, coupon listing, customer edits to restricted cart, order item and payment method fields, deletion side effects and the admin routes.
Contributed modules that add their own reference fields have to lock them down themselves. The restricted_fields mechanism is extensible: modules register their fields through hook_entity_type_build(), the hook the fix itself uses for the order's coupons field. Check the modules your site runs.
How do I check if I was compromised?
The advisory gives one instruction: audit your orders for inconsistencies. No indicators, request patterns or actor details are published, so the audit works from the data model:
- List variations whose
product_idchanged to a product owned by a different account than the one that created the variation. Ownership flips are the takeover signature. - List coupons whose
promotion_iddiffers from the promotion that originally issued them, and promotions whose coupon lists gained coupons they were never assigned. - Check for coupon redemptions that predate or bypass the promotion they were redeemed under, and for orders whose applied coupons belong to unrelated promotions.
- Review access logs for JSON:API and REST writes to commerce entities from accounts with limited permissions, and for entity autocomplete requests that returned coupon results.
- Confirm no promotion deletions removed coupons that still belonged to other promotions.
The access logs matter more than the code side: an attempt that failed leaves a 403 or 422 in the API log after any request reached the field, and a successful takeover in an affected version leaves a 2xx (200, or 201 for a JSON:API create).
Timeline
- 2024-08-22: Commerce Core 8.x-2.40 ships, the 2.x line's last release before this fix.
- 2026-10-28: Commerce Core 3.3.13 and 8.x-2.41 are tagged with the access-control fix.
- 2026-10-28: Drupal publishes SA-CONTRIB-2026-254, rated Highly critical 21/25, stating the flaw is actively exploited in the wild.
- 2026-10-29: CISA's KEV catalog still lists nothing for this flaw, checked in the feed.
- 2026-10-30: The CVE-2026-118539 record is published by the Drupal CNA, with no CVSS score, describing missing authorization allowing forceful browsing. The Hacker News covers the advisory the same day.
FAQ
Is a CVSS score available?
No. The CVE record published on 2026-10-30 carries no metrics, and the advisory uses Drupal's own risk scale: Highly critical, 21/25. Its vector uses Drupal's notation, not CVSS: AC:None/A:None/CI:Some/II:Some/E:Exploit/TD:All. The E:Exploit component reflects the in-the-wild statement.
Can anonymous visitors take over products?
No. Taking over products or variations needs an account with limited commerce permissions. The fix does stop customers, who need no commerce permission, from changing restricted fields such as the state and totals of their own carts through JSON:API or REST, and hidden variations no longer show up as omitted entries in JSON:API collections. The advisory frames the flaw for vendors, and its audit instruction targets order data.
My site only has administrators. Am I exposed?
Not to the vendor takeover paths, which need an account with some commerce permission but not the admin one. Customers are still a risk: any customer with a cart could change restricted fields of that cart, such as its state, totals and item prices, through JSON:API or REST. Patch now and audit orders as the advisory instructs.
Does the fix change how my store works?
It can, in one place: users who managed coupons on orders without the administration permission now type a coupon code instead of picking it from an autocomplete, because that listing leaked codes. Coupon redemption itself is unchanged.
What did the 2.x line miss?
Everything since August 2024, and this fix applies the same access-control layer there. The 2.41 tag carries the field restrictions for the older code paths, so the upgrade is the same one-step move.
Should I disable JSON:API while I patch?
It reduces the takeover surface, and read-only mode prevents writes entirely, but it is not a substitute for the update: the autocomplete route and the delete side effects did not need JSON:API. Patch first, then re-enable the API surface your consumers need.
Inventory your Drupal sites with Ostorlab
Ostorlab's attack surface discovery maps your internet-facing sites and their platforms, so you can find every public Drupal property and check its module versions before an attacker does.
References
- Drupal security advisory SA-CONTRIB-2026-254, Commerce Core, Highly critical, published 2026-10-28: https://www.drupal.org/sa-contrib-2026-254 (checked October 30, 2026)
- CVE-2026-118539 record, Drupal CNA, published 2026-10-30, missing authorization allowing forceful browsing: https://cveawg.mitre.org/api/cve/CVE-2026-118539 (checked October 30, 2026)
- Commerce Core 3.3.12 to 3.3.13 comparison, the access-control fix, its handlers and tests: https://git.drupalcode.org/project/commerce/-/compare/3.3.12...3.3.13 (checked October 30, 2026)
- Commerce Core 8.x-2.41 tag, created 2026-10-28: https://git.drupalcode.org/project/commerce/-/tags/8.x-2.41 (checked October 30, 2026)
- Commerce Core 8.x-2.40 tag, created 2024-08-22, the previous 2.x release: https://git.drupalcode.org/project/commerce/-/tags/8.x-2.40 (checked October 30, 2026)
- The Hacker News, "Hackers Actively Exploit Highly Critical Drupal Commerce Core Authorization Bypass Flaw", October 30, 2026: https://thehackernews.com/2026/10/hackers-actively-exploit-highly.html (checked October 30, 2026)
- CISA Known Exploited Vulnerabilities catalog JSON feed, checked for CVE-2026-118539 on October 30, 2026 (not listed): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (checked October 30, 2026)