{
    "document": {
        "category": "csaf_base",
        "csaf_version": "2.0",
        "distribution": {
            "tlp": {
                "label": "WHITE"
            }
        },
        "lang": "en",
        "notes": [
            {
                "category": "legal_disclaimer",
                "text": "The Netherlands Cyber Security Center (henceforth: NCSC-NL) maintains this portal to enhance access to its information and vulnerabilities. The use of this information is subject to the following terms and conditions:\n\nThe vulnerabilities disclosed in this portal are gathered by NCSC-NL from a variety of open sources, which the user can retrieve from other platforms. NCSC-NL makes every reasonable effort to ensure that the content of this portal is kept up to date, and that it is accurate and complete. Nevertheless, NCSC-NL cannot entirely rule out the possibility of errors, and therefore cannot give any warranty in respect of its completeness, accuracy or real-time keeping up-to-date. NCSC-NL does not control nor guarantee the accuracy, relevance, timeliness or completeness of information obtained from these external sources. The vulnerabilities disclosed in this portal are intended solely for the convenience of professional parties to take appropriate measures to manage the risks posed to the cybersecurity. No rights can be derived from the information provided therein.\n\nNCSC-NL and the Kingdom of the Netherlands assume no legal liability or responsibility for any damage resulting from either the use or inability of use of the vulnerabilities disclosed in this portal. This includes damage resulting from the inaccuracy of incompleteness of the information contained in it.\nThe information on this page is subject to Dutch law. All disputes related to or arising from the use of this portal regarding the disclosure of vulnerabilities will be submitted to the competent court in The Hague. This choice of means also applies to the court in summary proceedings."
            }
        ],
        "publisher": {
            "category": "coordinator",
            "contact_details": "cert@ncsc.nl",
            "name": "National Cyber Security Centre",
            "namespace": "https://www.ncsc.nl/"
        },
        "title": "CVE-2026-32638",
        "tracking": {
            "current_release_date": "2026-03-20T18:17:00.258512Z",
            "generator": {
                "date": "2026-02-17T15:00:00Z",
                "engine": {
                    "name": "V.E.L.M.A",
                    "version": "1.7"
                }
            },
            "id": "CVE-2026-32638",
            "initial_release_date": "2026-03-16T16:42:57.671307Z",
            "revision_history": [
                {
                    "date": "2026-03-16T16:42:57.671307Z",
                    "number": "1",
                    "summary": "CVE created.| Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (4).| CWES updated (1)."
                },
                {
                    "date": "2026-03-16T16:43:00.603380Z",
                    "number": "2",
                    "summary": "NCSC Score created."
                },
                {
                    "date": "2026-03-18T21:25:53.701917Z",
                    "number": "3",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-18T21:25:55.210985Z",
                    "number": "4",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-18T21:39:05.814356Z",
                    "number": "5",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products created (1).| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-18T21:39:08.268350Z",
                    "number": "6",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-18T23:09:21.739057Z",
                    "number": "7",
                    "summary": "References created (1)."
                },
                {
                    "date": "2026-03-19T07:35:44.366098Z",
                    "number": "8",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-19T15:31:20.884159Z",
                    "number": "9",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (5).| CWES updated (1)."
                },
                {
                    "date": "2026-03-19T21:08:05.654401Z",
                    "number": "10",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-20T04:46:01.492745Z",
                    "number": "11",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products connected (1).| References created (3).| CWES updated (1).| Unknown change."
                },
                {
                    "date": "2026-03-20T09:29:48.955654Z",
                    "number": "12",
                    "summary": "Source connected.| CVE status created. (valid)| EPSS created."
                },
                {
                    "date": "2026-03-20T18:16:56.011627Z",
                    "number": "13",
                    "summary": "Products created (1).| Product Identifiers created (1).| Exploits created (1)."
                },
                {
                    "date": "2026-03-20T18:16:59.106750Z",
                    "number": "14",
                    "summary": "NCSC Score updated."
                }
            ],
            "status": "interim",
            "version": "14"
        }
    },
    "product_tree": {
        "branches": [
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/<0.4.4",
                                "product": {
                                    "name": "vers:unknown/<0.4.4",
                                    "product_id": "CSAFPID-5873891",
                                    "product_identification_helper": {
                                        "cpe": "cpe:2.3:a:studiocms:studiocms:*:*:*:*:*:*:*:*"
                                    }
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "studiocms"
                    }
                ],
                "category": "vendor",
                "name": "studiocms"
            },
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/<0.4.4",
                                "product": {
                                    "name": "vers:unknown/<0.4.4",
                                    "product_id": "CSAFPID-5845495"
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "studiocms"
                    }
                ],
                "category": "vendor",
                "name": "withstudiocms"
            }
        ]
    },
    "vulnerabilities": [
        {
            "cve": "CVE-2026-32638",
            "cwe": {
                "id": "CWE-639",
                "name": "Authorization Bypass Through User-Controlled Key"
            },
            "notes": [
                {
                    "category": "description",
                    "text": "## 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.",
                    "title": "github - https://github.com/advisories/GHSA-xvf4-ch4q-2m24"
                },
                {
                    "category": "description",
                    "text": "StudioCMS is a server-side-rendered, Astro native, headless content management system. Prior to 0.4.4, 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. Version 0.4.4 fixes the issue.",
                    "title": "nvd - https://nvd.nist.gov/vuln/detail/CVE-2026-32638"
                },
                {
                    "category": "description",
                    "text": "StudioCMS is a server-side-rendered, Astro native, headless content management system. Prior to 0.4.4, 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. Version 0.4.4 fixes the issue.",
                    "title": "cveprojectv5 - https://www.cve.org/CVERecord?id=CVE-2026-32638"
                },
                {
                    "category": "description",
                    "text": "## 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.",
                    "title": "github - https://api.github.com/advisories/GHSA-xvf4-ch4q-2m24"
                },
                {
                    "category": "description",
                    "text": "StudioCMS is a server-side-rendered, Astro native, headless content management system. Prior to 0.4.4, 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. Version 0.4.4 fixes the issue.",
                    "title": "nvd - https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-32638"
                },
                {
                    "category": "description",
                    "text": "StudioCMS is a server-side-rendered, Astro native, headless content management system. Prior to 0.4.4, 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. Version 0.4.4 fixes the issue.",
                    "title": "cveprojectv5 - https://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/32xxx/CVE-2026-32638.json"
                },
                {
                    "category": "other",
                    "text": "0.00023",
                    "title": "EPSS"
                },
                {
                    "category": "other",
                    "text": "3.7",
                    "title": "NCSC Score"
                },
                {
                    "category": "other",
                    "text": "The value of the most recent CVSS (V3) score, Exploit code publicly available, There is exploit data available from source Nvd",
                    "title": "NCSC Score top decreasing factors"
                }
            ],
            "product_status": {
                "known_affected": [
                    "CSAFPID-5845495",
                    "CSAFPID-5873891"
                ]
            },
            "references": [
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://github.com/advisories/GHSA-xvf4-ch4q-2m24"
                },
                {
                    "category": "external",
                    "summary": "Source raw - github",
                    "url": "https://api.github.com/advisories/GHSA-xvf4-ch4q-2m24"
                },
                {
                    "category": "external",
                    "summary": "Source - nvd",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32638"
                },
                {
                    "category": "external",
                    "summary": "Source raw - nvd",
                    "url": "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-32638"
                },
                {
                    "category": "external",
                    "summary": "Source - cveprojectv5",
                    "url": "https://www.cve.org/CVERecord?id=CVE-2026-32638"
                },
                {
                    "category": "external",
                    "summary": "Source raw - cveprojectv5",
                    "url": "https://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/32xxx/CVE-2026-32638.json"
                },
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://api.github.com/advisories/GHSA-xvf4-ch4q-2m24"
                },
                {
                    "category": "external",
                    "summary": "Source - nvd",
                    "url": "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-32638"
                },
                {
                    "category": "external",
                    "summary": "Source - cveprojectv5",
                    "url": "https://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/32xxx/CVE-2026-32638.json"
                },
                {
                    "category": "external",
                    "summary": "Source - first",
                    "url": "https://api.first.org/data/v1/epss?limit=10000&offset=0"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd",
                    "url": "https://github.com/withstudiocms/studiocms/security/advisories/GHSA-xvf4-ch4q-2m24"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd",
                    "url": "https://github.com/withstudiocms/studiocms/commit/aebe8bcb3618bb07c6753e3f5c982c1fe6adea64"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd",
                    "url": "https://github.com/withstudiocms/studiocms/releases/tag/studiocms@0.4.4"
                },
                {
                    "category": "external",
                    "summary": "Reference - github",
                    "url": "https://github.com/advisories/GHSA-xvf4-ch4q-2m24"
                },
                {
                    "category": "external",
                    "summary": "Reference - github",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32638"
                }
            ],
            "scores": [
                {
                    "cvss_v3": {
                        "version": "3.1",
                        "vectorString": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
                        "baseScore": 2.7,
                        "baseSeverity": "LOW"
                    },
                    "products": [
                        "CSAFPID-5845495",
                        "CSAFPID-5873891"
                    ]
                }
            ],
            "title": "CVE-2026-32638"
        }
    ]
}