Channel-scoped administrators can read the global administrator directory
Resolver-level permission checks did not scope the selected Administrator rows
CVSS 3.1 base score
HighVector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Summary
Source
AdministratorResolver.administrator()
packages/core/src/api/resolvers/admin/administrator.resolver.ts:40
Sink
AdministratorService.findOne()
packages/core/src/service/services/administrator.service.ts:95
An administrator limited to one Vendure channel could list and retrieve administrators assigned exclusively to other channels, exposing staff names, email identifiers, role codes, permissions, and the global authorization structure.
Root cause. The GraphQL resolvers required ReadAdministrator only for the active request channel. AdministratorService.findAll() and findOne() then queried the global Administrator table without intersecting target roles and channels with the active user's authority.
Remediation evidence. GHSA-37j3-p93w-fq6w states that the issue was reported by the Winfunc Security Team on 17 July 2026 (report ASI7P966C). Vendure fixed it in 3.7.3 with commit 3bff2dd, which applies one visibility rule to administrator lists, direct reads, and dependent mutations.
Severity
- Attack vectorAV
- Network
- Attack complexityAC
- Low
- Privileges requiredPR
- Low
- User interactionUI
- None
- ScopeS
- Changed
- ConfidentialityC
- High
- IntegrityI
- None
- AvailabilityA
- None
Metric values as published in the disclosure vector. Meters show how far each value raises exposure.
Source-to-sink trace
- Source · attacker-controlledpackages/
core/ src/ api/ resolvers/ admin/ administrator.resolver.ts:40 AdministratorResolver.administrator()
- Step 01packages/
core/ src/ api/ resolvers/ admin/ administrator.resolver.ts:40 A channel-A administrator passes the channel token and satisfies
ReadAdministratoron that active channel.typescript @Query() @Allow(Permission.ReadAdministrator) administrator(ctx, args) { return this.service.findOne(ctx, args.id); } - Step 02packages/
core/ src/ service/ services/ administrator.service.ts:64 The vulnerable list query selected all non-deleted global administrator rows before pagination.
typescript return this.listQueryBuilder.build(Administrator, options, { relations: ['user', 'user.roles'], where: { deletedAt: IsNull() }, ctx, }).getManyAndCount(); - Step 03packages/
core/ src/ service/ services/ administrator.service.ts:95 The direct lookup likewise constrained only the object ID and soft-delete state, not role/channel visibility.
typescript repository.findOne({ relations: ['user', 'user.roles'], where: { id: administratorId, deletedAt: IsNull() }, }); - Sinkpackages/
core/ src/ service/ services/ administrator.service.ts:95 AdministratorService.findOne()
Impact
Reported impact
The request breaks marketplace/channel confidentiality by disclosing the global privileged staff directory and role structure. Those identifiers can support targeted follow-on attacks, but this entry claims only the independently demonstrated cross-channel read impact.
Attack surface
Vendure Admin GraphQL API deployments using multiple channels and channel-scoped administrator roles.
Preconditions
The attacker needs their own limited administrator account with ReadAdministrator on one channel. No cross-channel role, victim identifier, or SuperAdmin credential is required for the list path.
Attack path
- 1
Authenticate as a legitimate channel-limited administrator.
- 2
Select the attacker's permitted channel with the normal channel-token header.
- 3
Query
administratorsand request identifiers, email addresses, and nested roles. - 4
Observe administrators and roles assigned only to other channels.
- 5
Optionally use a returned ID with the direct
administrator(id:)query.
Proof of concept
- 01
Environment setup
Run Vendure before
3.7.3with channel A and channel B, each with separate staff, plus a channel-A-only administrator. - 02
Target configuration
Grant the attacker
ReadAdministratoronly on channel A and keep the victim's roles exclusively on channel B. - 03
Exploit delivery
Authenticate as the attacker and query
administrators { items { id emailAddress user { identifier roles { code permissions } } } }with channel A active. - 04
Expected response
The vulnerable build returns the channel-B administrator and role data in the channel-A response.
- 05
Outcome
Vendure
3.7.3filters lists before count/sort/pagination and makes direct lookup return no row unless the active user holds every permission of every target role on its channels.
Remediation
Guidance
Upgrade @vendure/core to 3.7.3 or later. Centralize Administrator visibility and apply it before pagination to list, direct-read, update, delete, and role-assignment paths.
const qb = listQueryBuilder.build(Administrator, options, {where: { deletedAt: IsNull() },ctx,});return qb.getManyAndCount();const qb = listQueryBuilder.build(Administrator, options, { ctx });await this.restrictToVisibleAdministrators(ctx, qb);const [items, totalItems] = await qb.getManyAndCount();
Check the upstream record for the project's current remediation status.
Investigate the paths that matter in your codebase.
Winfunc can trace relevant code paths, preserve supporting evidence, and prepare remediation suggestions for engineering review within an agreed scope.
