All posts
Forward Development

Vendure 3.7.2 patches a critical privilege escalation

Vendure 3.7.2 fixes four reported vulnerabilities and tightens channel scoping on update and delete paths — here is what to test before you ship it.

  • Vendure
  • Migrations
  • Operations

If you run Vendure with more than one channel and you have handed out the UpdateAdministrator permission to anyone below SuperAdmin, you have a live privilege escalation on your hands until you upgrade. That is the headline of v3.7.2: four reported vulnerabilities fixed, one of them critical, and the critical one is the kind that turns a junior account into a SuperAdmin without a password reset email ever landing in the right inbox.

The release notes call it a patch release. It is, in the sense that there are no breaking API changes and no migrations to run. It is not a drop-in bump, because two of the fixes change what your channel-restricted administrators are allowed to do, and one changes who is allowed to edit whom. If you have custom code or custom roles built around the old, looser behaviour, the upgrade will surface that on a Tuesday afternoon rather than in staging — unless you go looking first.

The critical fix is a full account takeover, not a theoretical one

GHSA-v85r-wfgv-jcqc: an administrator holding only the delegated UpdateAdministrator permission could reset any other administrator's password, including the SuperAdmin's, and then log in as them. There is no clever chaining required. The permission that exists so a store manager can fix a colleague's account was also sufficient to take over the account that controls everything.

Work out your exposure by answering one question: does any role other than SuperAdmin carry UpdateAdministrator? If the answer is yes, treat this as an incident-adjacent patch and ship it this week. If the answer is no, you are still upgrading, just with less urgency, because the other three fixes matter too.

The high-severity one is GHSA-hc75-2v4j-x372: the Admin API adjustDraftOrderLine mutation was reachable by unauthenticated callers, who could change line quantities and custom fields on orders they did not own. Unauthenticated reachability on an Admin API mutation is the sort of thing that shows up in access logs after the fact. If your Admin API is exposed to the internet — and for most deployments it is, behind nothing more than the auth layer that just failed to fire — pull the logs for that mutation while you are staging the upgrade.

The two medium fixes are the ones that will change your behaviour

Both mediums are cross-channel IDOR. GHSA-fp4j-ff6j-9793 covers deletes: a channel-restricted administrator could delete another channel's promotions and facet values by enumerating ids. GHSA-rgjm-ff27-p2hf covers writes: the same class of administrator could modify another channel's assets and stock locations, again by guessing ids.

The fixes are channel-scope guards added in two places. StockLocation and Asset update() picked up a guard in #5017. The FacetValue, ProductOption and Promotion delete paths picked one up in #5043. Read that list again with your own codebase in mind, because those are exactly the entities that plugin authors reach for. Asset in particular gets touched by any custom media pipeline, and Promotion gets touched by anything that generates or reaps campaign data on a schedule.

The failure mode is not an exception you will see in a unit test. It is a service call that used to succeed and now returns an error or a no-op because the RequestContext you passed in is scoped to a channel that does not own the entity. A background job built with a synthetic context, a webhook handler that reconstructs a context from a stored channel token, an import script that runs against the default channel and writes assets destined for a sub-channel — all of these worked before and may not now.

Audit your own code before you audit the upgrade

Grep for the service methods, not the GraphQL mutations. The mutations were always scoped; the services are where custom code enters. Start with these:

  • assetService.update, and anything that constructs an Asset and reassigns it
  • stockLocationService.update
  • promotionService.delete, facetValueService.delete, productOptionService.delete
  • Every place you build a RequestContext by hand rather than receiving one from a resolver

That last bullet is the real audit. If your plugin creates a context for a specific channel, confirm that channel is the one that owns the entity you are about to write. If your plugin creates a context for the default channel and then operates on entities across all channels, that pattern is now broken by design, and it was a cross-channel write vulnerability in your own code before this release closed it in core.

Delegated admin roles will start getting rejected

The fix for the critical advisory tightens updateAdministrator: an administrator can now only update another administrator if they hold all of the target's permissions on all of the target's channels. Updating a SuperAdmin therefore requires a SuperAdmin caller.

If you have delegated UpdateAdministrator to a role that edits higher-privileged accounts, those operations are rejected after the upgrade. There are two legitimate resolutions: grant the role the permissions it actually needs, or move the operation to a SuperAdmin. There is no third option that keeps the old behaviour, and you should not want one. Enumerate your roles before upgrading so the rejection is a decision you made rather than a support ticket.

Stage the upgrade with a channel isolation regression check

Upgrade all @vendure/* packages together to 3.7.2 — mixed versions across @vendure/core, plugins, and the dashboard are a reliable source of confusing failures. No migrations, no config changes.

Before promoting, run a deliberate isolation check on staging with at least two channels and a real channel-restricted administrator:

  1. As the restricted admin, attempt to update an Asset and a StockLocation belonging to the other channel by id. Both must fail.
  2. Attempt to delete a Promotion, a FacetValue, and a ProductOption in the other channel by id. All must fail.
  3. Run every scheduled job and import path that writes to channel-scoped entities. Watch for silent no-ops, not just thrown errors.
  4. Exercise each non-SuperAdmin role that holds UpdateAdministrator against the accounts it is expected to manage.
  5. If you use the BullMQ job queue plugin, page through the job list. #5014 rewrote that query — it previously dropped jobs from pages when results spanned list-stored and sorted-set-stored states, mixed raw delayed scores with plain timestamps in the ordering, and crashed above roughly 8000 waiting jobs. Verify your monitoring still reads what it expects.

That check is worth keeping as a permanent test, not a one-off. Channel isolation is the invariant most likely to be broken by a plugin nobody has read in a year, and it fails quietly.

What this says about picking Vendure in the first place

A release that names four advisories, credits the researchers, and documents exactly which permission semantics changed is a healthy signal, not a worrying one. The alternative — a platform where cross-channel IDOR gets folded into a changelog line reading "various security improvements" — is the one that should make you nervous. We weigh this kind of release hygiene heavily when we help a merchant choose a target platform, and it is a large part of why Vendure and Medusa.js are both on our list for migrations off Magento and other legacy stacks.

It also argues for treating multi-channel as an architectural commitment rather than a configuration toggle. If your channel model exists because two brands share a catalogue, the isolation between them is a security boundary and deserves tests that say so.

Upgrade to 3.7.2 now if any non-SuperAdmin role holds UpdateAdministrator. Otherwise, schedule it inside the week, and spend the staging time on the channel-scope audit rather than on smoke-testing the checkout. If you are mid-migration onto Vendure and want a second pair of eyes on how your plugins construct request contexts before this lands in production, get in touch.

Need this done on a real stack?

Magento 2, Adobe Commerce, migrations to Medusa.js or Vendure, enterprise Next.js, WordPress, and AI automation.

Contact us