GitHub Advisoryadvisory · npm
GHSA-xvf4-ch4q-2m24 StudioCMS REST getUsers Exposes Owner Account Records to Admin Tokens
LOW
Official page ↗
- Aliases
- CVE-2026-32638
- Ecosystem
- npm
- Packages
- studiocms
- Severity
- Low
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N
- Weaknesses
- CWE-639
- Reviewed
- 2026-03-16
Affected packages
• npm studiocms: < 0.4.4 → fixed in 0.4.4
Description
Summary
The REST API `getUsers` endpoint in StudioCMS uses the attacker-controlled `rank` query parameter to decide whether owner accounts should be filtered from the result set. As a result, an admin token can request `rank=owner` and receive owner account records, including IDs, usernames, display names, and email addresses, even though the adjacent `getUser` endpoint correctly blocks admins from viewing owner users. This is an authorization inconsistency inside the same user-management surface.
Details
Vulnerable Code Path
File: `D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts`, lines 1605-1647
```ts
.handle(
'getUsers',
Effect.fn(
function* ({ urlParams: { name, rank, username } }) {
if (!restAPIEnabled) {
return yield* new RestAPIError({ error: 'Endpoint not found' });
}
const [sdk, user] = yield* Effect.all([SDKCore, CurrentRestAPIUser]);
if (user.rank !== 'owner' && user.rank !== 'admin') {
return yield* new RestAPIError({ error: 'Unauthorized' });
}
const allUsers = yield* sdk.GET.users.all();
let data = allUsers.map(...);
if (rank !== 'owner') {
data = data.filter((user) => user.rank !== 'owner');
}
if (rank) {
data = data.filter((user) => user.rank === rank);
}
return data;
},
```
The `rank` variable in `if (rank !== 'owner')` is the request query parameter, not the caller's privilege level. An admin can therefore pass `rank=owner`, skip the owner-filtering branch, and then have the second `if (rank)` branch return only owner accounts.
Adjacent Endpoint Shows Intended Security Boundary
File: `D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts`, lines 1650-1710
```ts
const existingUserRankIndex = availablePermissionRanks.indexOf(existingUserRank);
const loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);
if (loggedInUserRankIndex '
```
Expected behavior:
• owner records should be excluded for admin callers, consistent with `getUser`
Actual behavior:
• the response contains owner user objects, including email addresses and user IDs
Local Validation of the Exact Handler Logic
I validated the filtering logic locally with the same conditions used by `getUsers` and `getUser`.
Observed output:
```json
{
"admin_getUsers_rank_owner": [
{
"email": "[email protected]",
"id": "owner-1",
"name": "Site Owner",
"rank": "owner",
"username": "owner1"
}
],
"admin_getUser_owner": "Unauthorized to view user with higher rank"
}
```
This demonstrates the authorization mismatch clearly:
• bulk listing with `rank=owner` exposes owner records
• direct access to a single owner record is denied
Impact
• Owner Account Enumeration: Admin tokens can recover owner user IDs, usernames, display names, and email addresses.
• Authorization Boundary Bypass: The REST collection endpoint bypasses the stricter per-record rank check already implemented by `getUser`.
• Chaining Value: Exposed owner contact data can support phishing, account-targeting, and admin-to-owner pivot attempts in deployments that treat owner identities as higher-trust principals.
Recommended Fix
Apply rank filtering based on the caller's role, not on the request query parameter, and reuse the same privilege rule as `getUser`.
Example fix:
```ts
const loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);
data = data.filter((candidate) => {
const candidateRankIndex = availablePermissionRanks.indexOf(candidate.rank);
return loggedInUserRankIndex > candidateRankIndex;
});
if (rank) {
data = data.filter((candidate) => candidate.rank === rank);
}
```
At minimum, replace:
```ts
if (rank !== 'owner') {
data = data.filter((user) => user.rank !== 'owner');
}
```
with a check tied to `user.rank` rather than the query parameter.
Related
Alias · CVE (1)
- CVE-2026-32638 StudioCMS is a server-side-rendered, Astro native, headless content management system. vulnerability
Weakness · CWE (1)
- CWE-639 Authorization Bypass Through User-Controlled Key weakness
References
- https://github.com/withstudiocms/studiocms/security/advisories/GHSA-xvf4-ch4q-2m24 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-32638 advisory
- https://github.com/withstudiocms/studiocms/commit/aebe8bcb3618bb07c6753e3f5c982c1fe6adea64 web
- https://github.com/withstudiocms/studiocms package
- https://github.com/withstudiocms/studiocms/releases/tag/[email protected] web