Skip to content

Commit 6dbad14

Browse files
1 parent 32cacf6 commit 6dbad14

File tree

3 files changed

+88
-4
lines changed

3 files changed

+88
-4
lines changed

advisories/github-reviewed/2026/03/GHSA-j478-p7vq-3347/GHSA-j478-p7vq-3347.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-j478-p7vq-3347",
4-
"modified": "2026-03-12T20:33:28Z",
4+
"modified": "2026-03-16T16:38:02Z",
55
"published": "2026-03-12T20:33:28Z",
66
"aliases": [
77
"CVE-2026-32320"
@@ -40,13 +40,21 @@
4040
"type": "WEB",
4141
"url": "https://github.com/ellanetworks/core/security/advisories/GHSA-j478-p7vq-3347"
4242
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32320"
46+
},
4347
{
4448
"type": "PACKAGE",
4549
"url": "https://github.com/ellanetworks/core"
4650
},
4751
{
4852
"type": "WEB",
4953
"url": "https://github.com/ellanetworks/core/releases/tag/v1.5.1"
54+
},
55+
{
56+
"type": "WEB",
57+
"url": "https://pkg.go.dev/vuln/GO-2026-4691"
5058
}
5159
],
5260
"database_specific": {
@@ -56,6 +64,6 @@
5664
"severity": "MODERATE",
5765
"github_reviewed": true,
5866
"github_reviewed_at": "2026-03-12T20:33:28Z",
59-
"nvd_published_at": null
67+
"nvd_published_at": "2026-03-13T19:54:42Z"
6068
}
6169
}

advisories/github-reviewed/2026/03/GHSA-m9pm-w3gv-c68f/GHSA-m9pm-w3gv-c68f.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"schema_version": "1.4.0",
33
"id": "GHSA-m9pm-w3gv-c68f",
4-
"modified": "2026-03-12T20:33:22Z",
4+
"modified": "2026-03-16T16:37:38Z",
55
"published": "2026-03-12T20:33:22Z",
66
"aliases": [
77
"CVE-2026-32319"
@@ -40,13 +40,21 @@
4040
"type": "WEB",
4141
"url": "https://github.com/ellanetworks/core/security/advisories/GHSA-m9pm-w3gv-c68f"
4242
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32319"
46+
},
4347
{
4448
"type": "PACKAGE",
4549
"url": "https://github.com/ellanetworks/core"
4650
},
4751
{
4852
"type": "WEB",
4953
"url": "https://github.com/ellanetworks/core/releases/tag/v1.5.1"
54+
},
55+
{
56+
"type": "WEB",
57+
"url": "https://pkg.go.dev/vuln/GO-2026-4692"
5058
}
5159
],
5260
"database_specific": {
@@ -56,6 +64,6 @@
5664
"severity": "HIGH",
5765
"github_reviewed": true,
5866
"github_reviewed_at": "2026-03-12T20:33:22Z",
59-
"nvd_published_at": null
67+
"nvd_published_at": "2026-03-13T19:54:42Z"
6068
}
6169
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-xvf4-ch4q-2m24",
4+
"modified": "2026-03-16T16:37:42Z",
5+
"published": "2026-03-16T16:37:42Z",
6+
"aliases": [
7+
"CVE-2026-32638"
8+
],
9+
"summary": "StudioCMS REST getUsers Exposes Owner Account Records to Admin Tokens",
10+
"details": "## Summary\n\nThe 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.\n\n## Details\n\n### Vulnerable Code Path\n\nFile: `D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts`, lines 1605-1647\n\n```ts\n.handle(\n 'getUsers',\n Effect.fn(\n function* ({ urlParams: { name, rank, username } }) {\n if (!restAPIEnabled) {\n return yield* new RestAPIError({ error: 'Endpoint not found' });\n }\n const [sdk, user] = yield* Effect.all([SDKCore, CurrentRestAPIUser]);\n\n if (user.rank !== 'owner' && user.rank !== 'admin') {\n return yield* new RestAPIError({ error: 'Unauthorized' });\n }\n\n const allUsers = yield* sdk.GET.users.all();\n let data = allUsers.map(...);\n\n if (rank !== 'owner') {\n data = data.filter((user) => user.rank !== 'owner');\n }\n\n if (rank) {\n data = data.filter((user) => user.rank === rank);\n }\n\n return data;\n },\n```\n\nThe `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.\n\n### Adjacent Endpoint Shows Intended Security Boundary\n\nFile: `D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts`, lines 1650-1710\n\n```ts\nconst existingUserRankIndex = availablePermissionRanks.indexOf(existingUserRank);\nconst loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);\n\nif (loggedInUserRankIndex <= existingUserRankIndex) {\n return yield* new RestAPIError({\n error: 'Unauthorized to view user with higher rank',\n });\n}\n```\n\n`getUser` correctly blocks an admin from viewing an owner record. `getUsers` bypasses that boundary for bulk enumeration.\n\n### Sensitive Fields Returned\n\nThe `getUsers` response includes:\n\n- `id`\n- `email`\n- `name`\n- `username`\n- `rank`\n- timestamps and profile URL/avatar fields when present\n\nThis is enough to enumerate all owner accounts and target them for phishing, social engineering, or follow-on attacks against out-of-band workflows.\n\n## PoC\n\n### HTTP PoC\n\nUse any admin-level REST API token:\n\n```bash\ncurl -X GET 'http://localhost:4321/studiocms_api/rest/v1/secure/users?rank=owner' \\\n -H 'Authorization: Bearer <admin-api-token>'\n```\n\nExpected behavior:\n- owner records should be excluded for admin callers, consistent with `getUser`\n\nActual behavior:\n- the response contains owner user objects, including email addresses and user IDs\n\n### Local Validation of the Exact Handler Logic\n\nI validated the filtering logic locally with the same conditions used by `getUsers` and `getUser`.\n\nObserved output:\n\n```json\n{\n \"admin_getUsers_rank_owner\": [\n {\n \"email\": \"owner@example.test\",\n \"id\": \"owner-1\",\n \"name\": \"Site Owner\",\n \"rank\": \"owner\",\n \"username\": \"owner1\"\n }\n ],\n \"admin_getUser_owner\": \"Unauthorized to view user with higher rank\"\n}\n```\n\nThis demonstrates the authorization mismatch clearly:\n- bulk listing with `rank=owner` exposes owner records\n- direct access to a single owner record is denied\n\n## Impact\n\n- **Owner Account Enumeration:** Admin tokens can recover owner user IDs, usernames, display names, and email addresses.\n- **Authorization Boundary Bypass:** The REST collection endpoint bypasses the stricter per-record rank check already implemented by `getUser`.\n- **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.\n\n## Recommended Fix\n\nApply rank filtering based on the caller's role, not on the request query parameter, and reuse the same privilege rule as `getUser`.\n\nExample fix:\n\n```ts\nconst loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);\n\ndata = data.filter((candidate) => {\n const candidateRankIndex = availablePermissionRanks.indexOf(candidate.rank);\n return loggedInUserRankIndex > candidateRankIndex;\n});\n\nif (rank) {\n data = data.filter((candidate) => candidate.rank === rank);\n}\n```\n\nAt minimum, replace:\n\n```ts\nif (rank !== 'owner') {\n data = data.filter((user) => user.rank !== 'owner');\n}\n```\n\nwith a check tied to `user.rank` rather than the query parameter.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "studiocms"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "0.4.4"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.4.3"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/withstudiocms/studiocms/security/advisories/GHSA-xvf4-ch4q-2m24"
45+
},
46+
{
47+
"type": "WEB",
48+
"url": "https://github.com/withstudiocms/studiocms/commit/aebe8bcb3618bb07c6753e3f5c982c1fe6adea64"
49+
},
50+
{
51+
"type": "PACKAGE",
52+
"url": "https://github.com/withstudiocms/studiocms"
53+
},
54+
{
55+
"type": "WEB",
56+
"url": "https://github.com/withstudiocms/studiocms/releases/tag/studiocms@0.4.4"
57+
}
58+
],
59+
"database_specific": {
60+
"cwe_ids": [
61+
"CWE-639"
62+
],
63+
"severity": "LOW",
64+
"github_reviewed": true,
65+
"github_reviewed_at": "2026-03-16T16:37:42Z",
66+
"nvd_published_at": null
67+
}
68+
}

0 commit comments

Comments
 (0)