Skip to content
winfunc
Disclosure record · Vendure

Channel-scoped administrators can read the global administrator directory

Resolver-level permission checks did not scope the selected Administrator rows

VendureDisclosedSource record

CVSS 3.1 base score

High
7.7/ 10

Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

Record

Project
Vendure
Severity
High7.7
CVE
None listed
Disclosed
Trace
3 steps

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

  1. Source · attacker-controlledpackages/core/src/api/resolvers/admin/administrator.resolver.ts:40

    AdministratorResolver.administrator()

  2. Step 01packages/core/src/api/resolvers/admin/administrator.resolver.ts:40

    A channel-A administrator passes the channel token and satisfies ReadAdministrator on that active channel.

    typescript
    @Query()
    @Allow(Permission.ReadAdministrator)
    administrator(ctx, args) { return this.service.findOne(ctx, args.id); }
    
  3. 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();
    
  4. 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() },
    });
    
  5. 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. 1

    Authenticate as a legitimate channel-limited administrator.

  2. 2

    Select the attacker's permitted channel with the normal channel-token header.

  3. 3

    Query administrators and request identifiers, email addresses, and nested roles.

  4. 4

    Observe administrators and roles assigned only to other channels.

  5. 5

    Optionally use a returned ID with the direct administrator(id:) query.

Proof of concept

Reproduction5 stages
  1. 01

    Environment setup

    Run Vendure before 3.7.3 with channel A and channel B, each with separate staff, plus a channel-A-only administrator.

  2. 02

    Target configuration

    Grant the attacker ReadAdministrator only on channel A and keep the victim's roles exclusively on channel B.

  3. 03

    Exploit delivery

    Authenticate as the attacker and query administrators { items { id emailAddress user { identifier roles { code permissions } } } } with channel A active.

  4. 04

    Expected response

    The vulnerable build returns the channel-B administrator and role data in the channel-A response.

  5. 05

    Outcome

    Vendure 3.7.3 filters 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.

Before and after−5+3
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();
View original source record

Check the upstream record for the project's current remediation status.

Your codebase

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.