github vendurehq/vendure v3.7.3

4 hours ago

Vendure v3.7.3 is a patch release containing fixes for eleven reported vulnerabilities (four critical, two high, five medium), the seller Order channel-scoping work for multi-vendor setups, corrections to how shipping lines are handled when a shipping method is deleted or unassigned, monorepo support in the CLI, and a batch of dashboard fixes.

Several of the security fixes change behaviour. If you run a multi-channel deployment, or you register an external/SSO authentication strategy, read Behaviour Changes below before upgrading.

Security

Fixes for the following reported vulnerabilities are in 3.7.3. If you are on an earlier 3.x version, upgrade.

Severity Vulnerability Advisory
Critical Unauthenticated account takeover: any customer account created through an external/SSO authentication strategy could be taken over by "registering" that account's email address with an attacker-chosen password GHSA-wr5h-x3x6-4h23
Critical Cross-channel payment IDOR: an administrator scoped to one channel could issue refunds and perform payment and fulfillment transitions against orders belonging to another channel, moving money out of another tenant's payment gateway GHSA-7qvr-c5vf-xxfh
Critical Cross-channel entity capture: an administrator could assign a Product, Collection, Facet, ProductOptionGroup or ProductVariant belonging to another channel into their own channel, gaining ongoing edit and delete control over it GHSA-422x-jq57-j238
High Cross-channel data disclosure: the duplicateEntity mutation copied the source entity without a channel filter, so an administrator could obtain a full copy of an entity from a channel they cannot access GHSA-f94w-2928-x43p
High Administrator directory disclosure: a channel-scoped administrator could read the whole administrator directory through the Admin API, including names, email addresses and role permissions of staff belonging only to other channels GHSA-37j3-p93w-fq6w
Critical Cross-channel channel management: an administrator holding UpdateChannel or DeleteChannel could rename or permanently delete any channel in the system, regardless of which channels their role was scoped to GHSA-22x4-937q-5fr5
Medium Cross-channel catalog write: a channel-scoped administrator could pass the id of a ProductOptionGroup belonging to another channel to createProductOption, linking a new option into another channel's option group GHSA-gg28-cx38-jxxr
Medium Account enumeration by login timing: the mitigation added in 3.5.3 did not cover accounts which exist but have no native password, so those accounts still answered measurably faster than a non-existent address GHSA-c63h-3vvx-48ph
Medium Permissive CORS default: the default configuration reflects any Origin back with Access-Control-Allow-Credentials: true, and Apollo's CSRF prevention was disabled, allowing Login CSRF and cross-origin response reading GHSA-vr2h-89r2-9rwv
Medium Session token disclosure via job data: the serialized RequestContext stored in job data contained the session token, which any administrator holding ReadSettings or ReadSystem could read and reuse GHSA-32jm-mf7r-7qw5
Medium Stored XSS via SVG upload: @vendure/asset-server-plugin served uploaded SVG files inline with an executable content type, so a malicious upload executed script when its URL was opened GHSA-f4r3-h6jf-4m29

file-type has also been bumped to ^21.3.1 in both @vendure/core and @vendure/asset-server-plugin (#5099) to pick up the fix for GHSA-5v7r-6r5c-r473, an infinite loop on malformed ASF input. This bump renames four MIME types — see Migration below.

Our thanks to the researchers who reported these responsibly.

Core

  • Seller Order channel scoping for multi-vendor setups (#5100, closes #4117) — price adjustments are now applied in the seller's channel, shipping lines are preserved when an order is split, shipping promotions are re-applied to seller orders, and the transaction is preserved when scoping the seller order context.
  • Shipping lines are no longer removed from an order when the shipping method is deleted, and active orders are recalculated when a shipping method is unassigned from a channel (#4492, #4494).
  • Preserve OrderLine relation custom fields when merging orders (#5151).
  • Enqueue the apply-collection-filters job after the transaction commits (#5195), so the worker no longer reads a collection that is not yet visible.
  • Filter the SQLite search index by the request currency (#5149).
  • Prevent exponential re-merge of shared entity references during hydration (#5085).
  • Prevent a NaN discount amount on a cancelled modified OrderLine (#5101).
  • Price every ProductVariant in relation arrays, avoiding a spread RangeError on large arrays (#5058).
  • Check every element in isTranslatable and harden translateDeep against holes in arrays (#5059).
  • Propagate field descriptions to generated Filter and Sort parameters (#5065, closes #5032).
  • Invalidate the stock location channel id cache correctly, and handle invalidation failures (#5087, closes #3324).
  • Guard the initial job-queue status update, log the real error details, and release the concurrency slot even when the update fails (#5167, #5168).
  • Use the default Channel context when creating an Asset from a file stream (#4662).
  • Assign the SuperAdmin and Customer roles in ChannelService.create() (#5095).
  • Enforce the channel permission check on Promotion assign and remove (#5072), and guard against removing promotions from the default channel (#5096).
  • Enforce channel scope on ProductVariant soft-deletion (#5049).
  • Update better-sqlite3 for Node 24 (#5146).

Performance

  • Remove the per-request stock query stampede (#5224). MultiChannelStockLocationStrategy is the default since 3.1.0 and sits on the read path of every storefront product query. Its stock location channel id cache had no in-flight de-duplication, so every stock level on a page missed the cache before any of them had populated it and each issued its own query.

Dashboard

  • Entity selectors refetch on mount, so a newly created entity appears without a page reload (#5190, closes #5177, #5178, #5182).
  • The channel switcher refreshes after channels are deleted, and the active channel is recovered when the current one is deleted (#5181, #5228).
  • Source root resolution for extension compilation: compiled files stay inside outputPath for upward imports, the tsconfig baseUrl is remapped when the source root widens, and the filesystem root is preserved in commonAncestorDir (#5090, closes #5086).
  • Transform JSX when compiling the Vendure config and plugin config imports (closes #3726).
  • Manual payments: the transaction ID is now optional (#5027), the add-payment option is matched exactly (#5175), and the dialog close button works (#5133).
  • Treat readonly custom fields as optional in the Zod schema (#5057, closes #5045).
  • Use updateActiveAdministrator on the profile page, so an administrator without UpdateAdministrator can still edit their own profile (#5055).
  • Remove the arbitrary maximum of 1000 on promotion usage limits (#5226).
  • Forward transformQueryKey and view options from ListPage (#5066).
  • Refresh the tax category selector after mutations (#5178).
  • Show a loading indicator while assets upload.

CLI

  • Resolve hoisted dependencies in monorepo workspaces (#5141, closes #4842). Dependency checks now use require.resolve, detect the monorepo root, and walk up to the workspace root when a local node_modules is absent.

@vendure/create

  • Add a TanStack Start storefront option (#5144).
  • Defer the open import to avoid a Yarn PnP crash on Node 22 (#5207).
  • New projects are scaffolded with an explicit CORS origin allowlist read from a CORS_ORIGINS environment variable.

Asset Server Plugin

  • Add a background colour query parameter for transparent images (#4999).

Additions

  • apiOptions.csrfPrevention enables Apollo Server's built-in CSRF prevention on both APIs. It defaults to false; see Behaviour Changes for what enabling it requires of your clients.
  • Create a customer and address inline on a draft order (#4952).
  • Select multiple channels in the assign-to-channel follow-up dialog (#5036).

Behaviour Changes

No breaking API changes, but several of the security fixes tighten behaviour that was previously permissive.

Customer registration. registerCustomerAccount no longer stores the supplied password when an account already exists for that email address through another authentication strategy and has no password yet. A verification token is emailed to the address instead, and the password is set by passing that token to verifyCustomerAccount. This happens whatever the value of authOptions.requireVerification, so such a customer cannot log in immediately after registering. The caller-supplied firstName, lastName, phoneNumber and custom fields are now ignored whenever a User already exists for the email address.

Channel scoping. The following now throw when the target entity is not visible in the active channel, where they previously succeeded: duplicateEntity, the assign-to-channel and remove-from-channel mutations, updateChannel and deleteChannel, createProductOption, and the Order payment, refund, fulfillment and customer note operations. deleteProductVariant and deleteProductVariants now throw EntityNotFoundError for an id that is unknown or not in the active channel, where they previously returned DELETED silently. If you have delegated these permissions to channel-scoped roles which operate across channels, those operations will now be rejected.

Administrator visibility. The administrators and administrator queries now only return administrators whose roles the caller has authority over. A channel-scoped administrator no longer sees staff belonging only to other channels.

Asset serving. The asset server now sends X-Content-Type-Options: nosniff and a Content-Security-Policy on every asset, and serves markup assets (SVG, HTML, XML) with Content-Disposition: attachment. Opening such an asset's URL directly now downloads it rather than rendering it. Images embedded with <img src> are unaffected.

CORS and CSRF. Vendure now logs a warning at startup when apiOptions.cors reflects any origin together with credentials: true, which is the default. Set an explicit origin allowlist in production. The new apiOptions.csrfPrevention option defaults to false, so nothing changes unless you enable it. If you do, every client which uploads files or uses GET for queries must send the Apollo-Require-Preflight header. @vendure/admin-ui, @vendure/dashboard and @vendure/testing already do; a custom admin client built on apollo-upload-client does not unless you add it.

Migration

No database migrations are required. Upgrade all @vendure/* packages together to 3.7.3.

Two things to check after upgrading:

Job data may contain session tokens. The Admin API no longer returns the session token in job data, but tokens written before the upgrade are still present in existing job records. Purge settled job data and consider invalidating existing administrator sessions. The change which stops RequestContext.serialize() persisting the token in the first place alters the type of RequestContext.session, so it lands in the next minor release rather than this patch.

file-type v21 renames four MIME types to their IANA registrations: audio/x-flac becomes audio/flac, video/x-matroska becomes video/matroska, application/x-apache-arrow becomes application/vnd.apache.arrow.file, and application/x-parquet becomes application/vnd.apache.parquet. If you list any of the old values explicitly in assetOptions.permittedFileTypes, update them, otherwise those uploads will be rejected. The default wildcard config (image/*, video/*, audio/*, .pdf) is unaffected.

What's Changed

  • feat(dashboard): Support multi-channel selection in assign-to-channel follow-up by @Ryrahul in #5036
  • feat(asset-server-plugin): add background color query param for trans… by @Ryrahul in #4999
  • revert: feat(asset-server-plugin) background color query param (#4999) by @michaelbromley in #5088
  • fix(dashboard): treat readonly custom fields as optional in Zod schema by @Ryrahul in #5057
  • feat(dashboard): Allow creating customer and address inline on draft … by @Ryrahul in #4952
  • fix(dashboard): Forward transformQueryKey and view options from ListPage by @grolmus in #5066
  • fix(core): Enforce channel permission check on Promotion assign/remove by @latifniz in #5072
  • fix(core): Prevent exponential re-merge of shared entity references by @TheHypnoo in #5085
  • fix(dashboard): Use updateActiveAdministrator for profile page by @Ryrahul in #5055
  • docs(core): Reference GHSA-fp4j and GHSA-rgjm in 3.7.2 changelog by @HouseinIsProgramming in #5098
  • fix(dashboard): Show loading indicator when uploading assets by @latifniz in #4940
  • fix(core): Bump file-type to ^21.3.1 (GHSA-5v7r-6r5c-r473) by @HouseinIsProgramming in #5099
  • fix(core): Guard against removing promotions from default channel by @Ryrahul in #5096
  • fix(core): Prevent NaN discount amount on cancelled modified OrderLine by @TheHypnoo in #5101
  • fix(core): Check every element in isTranslatable, harden translateDeep against holes by @ryandiginomad in #5059
  • fix(core): Price every ProductVariant in relation arrays, avoid spread RangeError by @ryandiginomad in #5058
  • fix(core): Unassigning shipping lines from a channel breaks active orders by @kwerie in #4494
  • fix(core): Invalidate stock location channel id cache correctly by @ryandiginomad in #5087
  • fix(core): Apply seller Order price adjustments in the seller's Channel by @biggamesmallworld in #5100
  • fix(repo): Remove duplicate security report link by @dlhck in #5138
  • chore: remove sentence case commitlint rule by @dlhck in #5145
  • feat(create): add TanStack Start storefront option by @dlhck in #5144
  • fix(core): update better-sqlite3 for Node 24 by @dlhck in #5146
  • fix(core): filter SQLite search index by request currency by @dlhck in #5149
  • fix(core): Propagate field descriptions to generated Filter/Sort parameters by @grolmus in #5065
  • fix(dashboard): Close button on add payment dialog not working by @Ryrahul in #5133
  • fix(dashboard): transform JSX when compiling plugin config imports by @biggamesmallworld in #5152
  • fix(core): Enforce channel scope on product variant soft-deletion by @grolmus in #5049
  • fix(dashboard): optional transaction ID for manual payment by @knoid in #5027
  • docs: list supported storefront starters by @dlhck in #5158
  • fix(asset-server-plugin): harden SVG asset serving headers (GHSA-f4r3) by @grolmus in #5102
  • fix(core): assign SuperAdmin & Customer roles in ChannelService.create() by @Ryrahul in #5095
  • fix(cli): resolve hoisted dependencies in monorepo workspaces by @Ryrahul in #5141
  • fix(dashboard): match the add-payment option exactly by @biggamesmallworld in #5175
  • fix(dashboard): refresh tax category selector after mutations by @Ryrahul in #5178
  • fix(core): free job queue slot when status update fails by @wakqasahmed in #5168
  • fix(dashboard): keep compiled files inside outputPath for upward imports by @TheStreamCode in #5090
  • fix(dashboard): make entity selectors refetch on mount by @Ryrahul in #5190
  • fix(dashboard): refresh channel switcher after deleting channels by @Ryrahul in #5181
  • fix(core): use default Channel context when creating an Asset from a file stream by @kevmtt in #4662
  • fix(core): preserve OrderLine relation custom fields when merging orders by @deep231w in #5151
  • ci: automate dependency updates with dependabot by @michaelbromley in #5206
  • fix(dashboard): remove arbitrary 1000 max on promotion usage limits by @Ryrahul in #5226
  • chore(deps): bump the typeorm group with 3 updates by @dependabot[bot] in #5216
  • chore(deps): bump the graphql group with 4 updates by @dependabot[bot] in #5215
  • chore(deps-dev): bump the linting group with 3 updates by @dependabot[bot] in #5213
  • chore(deps): bump @ngx-translate/http-loader from 16.0.1 to 18.0.0 by @dependabot[bot] in #5223
  • fix(create): defer open import to avoid Yarn PnP crash on Node 22 by @michaelbromley in #5207
  • chore(deps-dev): bump typescript from 5.8.2 to 7.0.2 in /docs by @dependabot[bot] in #5211
  • fix(core): enqueue apply-collection-filters job after the transaction commits by @michaelbromley in #5195
  • chore(ci): ignore Angular Admin UI dependency families in Dependabot by @michaelbromley in #5230
  • ci: generate docs on the base branch instead of the PR branch by @michaelbromley in #5229
  • fix(dashboard): recover active channel after deleting the current one by @Ryrahul in #5228
  • ci: hold the MySQL e2e data directory in memory by @michaelbromley in #5233
  • chore(deps): bump @swc/core-linux-x64-gnu from 1.4.7 to 1.16.1 by @dependabot[bot] in #5235
  • perf(core): remove the per-request stock query stampede by @biggamesmallworld in #5224
  • chore(deps-dev): bump @types/pg from 8.16.0 to 8.23.1 in the types group by @dependabot[bot] in #5234

New Contributors

Full Changelog: v3.7.2...v3.7.3

Don't miss a new vendure release

NewReleases is sending notifications on new releases.