{
    "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-30945",
        "tracking": {
            "current_release_date": "2026-03-23T01:35:54.839882Z",
            "generator": {
                "date": "2026-02-17T15:00:00Z",
                "engine": {
                    "name": "V.E.L.M.A",
                    "version": "1.7"
                }
            },
            "id": "CVE-2026-30945",
            "initial_release_date": "2026-03-10T17:40:34.496763Z",
            "revision_history": [
                {
                    "date": "2026-03-10T17:40:34.496763Z",
                    "number": "1",
                    "summary": "CVE created.| Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| Products connected (1).| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-10T17:40:36.221779Z",
                    "number": "2",
                    "summary": "NCSC Score created."
                },
                {
                    "date": "2026-03-10T18:37:27.622551Z",
                    "number": "3",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (3).| CWES updated (1)."
                },
                {
                    "date": "2026-03-10T18:37:31.812231Z",
                    "number": "4",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-10T19:38:50.188403Z",
                    "number": "5",
                    "summary": "Unknown change."
                },
                {
                    "date": "2026-03-11T00:53:21.025024Z",
                    "number": "6",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (5).| CWES updated (1)."
                },
                {
                    "date": "2026-03-11T00:53:24.682509Z",
                    "number": "7",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-11T14:54:57.321311Z",
                    "number": "8",
                    "summary": "Source created.| CVE status created. (valid)| EPSS created."
                },
                {
                    "date": "2026-03-11T14:54:59.471589Z",
                    "number": "9",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-17T16:30:16.698635Z",
                    "number": "10",
                    "summary": "Products created (1).| Product Identifiers created (1).| Exploits created (1)."
                },
                {
                    "date": "2026-03-17T16:30:23.451169Z",
                    "number": "11",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-19T15:29:40.033559Z",
                    "number": "12",
                    "summary": "Source created.| CVE status created. (valid)| Description created for source.| CVSS created.| References created (5).| CWES updated (1)."
                },
                {
                    "date": "2026-03-19T15:29:42.985424Z",
                    "number": "13",
                    "summary": "NCSC Score updated."
                },
                {
                    "date": "2026-03-20T09:33:30.835070Z",
                    "number": "14",
                    "summary": "Source connected.| CVE status created. (valid)| EPSS created."
                }
            ],
            "status": "interim",
            "version": "14"
        }
    },
    "product_tree": {
        "branches": [
            {
                "branches": [
                    {
                        "branches": [
                            {
                                "category": "product_version_range",
                                "name": "vers:unknown/<0.4.0",
                                "product": {
                                    "name": "vers:unknown/<0.4.0",
                                    "product_id": "CSAFPID-5837993",
                                    "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.0",
                                "product": {
                                    "name": "vers:unknown/<0.4.0",
                                    "product_id": "CSAFPID-5775284"
                                }
                            }
                        ],
                        "category": "product_name",
                        "name": "studiocms"
                    }
                ],
                "category": "vendor",
                "name": "withstudiocms"
            }
        ]
    },
    "vulnerabilities": [
        {
            "cve": "CVE-2026-30945",
            "cwe": {
                "id": "CWE-639",
                "name": "Authorization Bypass Through User-Controlled Key"
            },
            "notes": [
                {
                    "category": "description",
                    "text": "StudioCMS is a server-side-rendered, Astro native, headless content management system. Prior to 0.4.0, the DELETE /studiocms_api/dashboard/api-tokens endpoint allows any authenticated user with editor privileges or above to revoke API tokens belonging to any other user, including admin and owner accounts. The handler accepts tokenID and userID directly from the request payload without verifying token ownership, caller identity, or role hierarchy. This enables targeted denial of service against critical integrations and automations. This vulnerability is fixed in 0.4.0.",
                    "title": "cveprojectv5 - https://www.cve.org/CVERecord?id=CVE-2026-30945"
                },
                {
                    "category": "description",
                    "text": "StudioCMS is a server-side-rendered, Astro native, headless content management system. Prior to 0.4.0, the DELETE /studiocms_api/dashboard/api-tokens endpoint allows any authenticated user with editor privileges or above to revoke API tokens belonging to any other user, including admin and owner accounts. The handler accepts tokenID and userID directly from the request payload without verifying token ownership, caller identity, or role hierarchy. This enables targeted denial of service against critical integrations and automations. This vulnerability is fixed in 0.4.0.",
                    "title": "nvd - https://nvd.nist.gov/vuln/detail/CVE-2026-30945"
                },
                {
                    "category": "description",
                    "text": "## Summary\nThe DELETE /studiocms_api/dashboard/api-tokens endpoint allows any authenticated user with editor privileges or above to revoke API tokens belonging to any other user, including admin and owner accounts. The handler accepts tokenID and userID directly from the request payload without verifying token ownership, caller identity, or role hierarchy. This enables targeted denial of service against critical integrations and automations.\n\n## Details\n#### Vulnerable Code\nThe following is the server-side handler for the `DELETE /studiocms_api/dashboard/api-tokens` endpoint (`revokeApiToken`):\n\n**File:** packages/studiocms/frontend/pages/studiocms_api/dashboard/api-tokens.ts (lines 58–99)\n**Version:** studiocms@0.3.0\n```\nDELETE: (ctx) =>\n    genLogger('studiocms/routes/api/dashboard/api-tokens.DELETE')(function* () {\n        const sdk = yield* SDKCore;\n\n        // Check if demo mode is enabled\n        if (developerConfig.demoMode !== false) {\n            return apiResponseLogger(403, 'Demo mode is enabled, this action is not allowed.');\n        }\n\n        // Get user data\n        const userData = ctx.locals.StudioCMS.security?.userSessionData;       // [1]\n\n        // Check if user is logged in\n        if (!userData?.isLoggedIn) {                                            // [2]\n            return apiResponseLogger(403, 'Unauthorized');\n        }\n\n        // Check if user has permission\n        const isAuthorized = ctx.locals.StudioCMS.security?.userPermissionLevel.isEditor;  // [3]\n        if (!isAuthorized) {\n            return apiResponseLogger(403, 'Unauthorized');\n        }\n\n        // Get Json Data\n        const jsonData = yield* readAPIContextJson<{\n            tokenID: string;                                                    // [4]\n            userID: string;                                                     // [5]\n        }>(ctx);\n\n        // Validate form data\n        if (!jsonData.tokenID) {\n            return apiResponseLogger(400, 'Invalid form data, tokenID is required');\n        }\n\n        if (!jsonData.userID) {\n            return apiResponseLogger(400, 'Invalid form data, userID is required');\n        }\n\n        // [6] Both user-controlled values passed directly — no ownership or identity checks\n        yield* sdk.REST_API.tokens.delete({ tokenId: jsonData.tokenID, userId: jsonData.userID });\n\n        return apiResponseLogger(200, 'Token deleted');                         // [7]\n    }),\n```\n**Analysis**\nThe handler shares the same class of authorization flaws found in the token generation endpoint, applied to a destructive operation:\n1. **Insufficient permission gate [1][2][3]:** The handler retrieves the session from ctx.locals.StudioCMS.security and only checks isEditor. Token revocation is a high-privilege operation that should require ownership of the token or elevated administrative privileges — not a generic editor-level gate.\n2. **No token ownership validation [4][6]:** The handler does not verify that jsonData.tokenID actually belongs to the jsonData.userID supplied in the payload. An attacker could enumerate or guess token IDs and revoke them regardless of ownership.\n3. **Missing caller identity check [5][6]:** The jsonData.userID from the payload is never compared against userData (the authenticated caller from [1]). Any editor can specify an arbitrary target user UUID and revoke their tokens.\n4. **No role hierarchy enforcement [6]:** There is no check preventing a lower-privileged user (editor) from revoking tokens belonging to higher-privileged accounts (admin, owner).\n5. **Direct pass-through to destructive operation [6][7]:** Both user-controlled parameters are passed directly to sdk.REST_API.tokens.delete() without any server-side validation, and the server responds with a generic success message, making this a textbook IDOR.\n\n## PoC\n**Environment**\n*User ID | Role*\n2450bf33-0135-4142-80be-9854f9a5e9f1 | owner\n39b3e7d3-5eb0-48e1-abdc-ce95a57b212c | editor\n\n**Attack — Editor Revokes Owner's API Token**\nAn authenticated editor sends the following request to revoke a token belonging to the owner:\n```\nDELETE /studiocms_api/dashboard/api-tokens HTTP/1.1\nHost: 127.0.0.1:4321\nCookie: auth_session=<editor_session_cookie>\nContent-Type: application/json\nAccept: application/json\nContent-Length: 98\n\n{\n  \"tokenID\": \"16a2e549-513b-40ac-8ca3-858af6118afc\",\n  \"userID\": \"2450bf33-0135-4142-80be-9854f9a5e9f1\"\n}\n```\n\n**Response (HTTP 200):**\n```\n{\"message\":\"Token deleted\"}\n```\nThe server confirmed deletion of the owner's token. The tokenID here refers to the internal token record identifier (UUID), not the JWT value itself. The editor's session cookie was sufficient to authorize this destructive action against a higher-privileged user.\n\n## Impact\n- **Denial of Service on integrations:** API tokens used in CI/CD pipelines, third-party integrations, or monitoring systems can be silently revoked, causing automated workflows to fail without warning.\n- **No audit trail:** The revocation is processed as a legitimate operation — the only evidence is the editor's own session, making attribution difficult without detailed request logging.",
                    "title": "github - https://github.com/advisories/GHSA-8rgj-vrfr-6hqr"
                },
                {
                    "category": "description",
                    "text": "## Summary\nThe DELETE /studiocms_api/dashboard/api-tokens endpoint allows any authenticated user with editor privileges or above to revoke API tokens belonging to any other user, including admin and owner accounts. The handler accepts tokenID and userID directly from the request payload without verifying token ownership, caller identity, or role hierarchy. This enables targeted denial of service against critical integrations and automations.\n\n## Details\n#### Vulnerable Code\nThe following is the server-side handler for the `DELETE /studiocms_api/dashboard/api-tokens` endpoint (`revokeApiToken`):\n\n**File:** packages/studiocms/frontend/pages/studiocms_api/dashboard/api-tokens.ts (lines 58–99)\n**Version:** studiocms@0.3.0\n```\nDELETE: (ctx) =>\n    genLogger('studiocms/routes/api/dashboard/api-tokens.DELETE')(function* () {\n        const sdk = yield* SDKCore;\n\n        // Check if demo mode is enabled\n        if (developerConfig.demoMode !== false) {\n            return apiResponseLogger(403, 'Demo mode is enabled, this action is not allowed.');\n        }\n\n        // Get user data\n        const userData = ctx.locals.StudioCMS.security?.userSessionData;       // [1]\n\n        // Check if user is logged in\n        if (!userData?.isLoggedIn) {                                            // [2]\n            return apiResponseLogger(403, 'Unauthorized');\n        }\n\n        // Check if user has permission\n        const isAuthorized = ctx.locals.StudioCMS.security?.userPermissionLevel.isEditor;  // [3]\n        if (!isAuthorized) {\n            return apiResponseLogger(403, 'Unauthorized');\n        }\n\n        // Get Json Data\n        const jsonData = yield* readAPIContextJson<{\n            tokenID: string;                                                    // [4]\n            userID: string;                                                     // [5]\n        }>(ctx);\n\n        // Validate form data\n        if (!jsonData.tokenID) {\n            return apiResponseLogger(400, 'Invalid form data, tokenID is required');\n        }\n\n        if (!jsonData.userID) {\n            return apiResponseLogger(400, 'Invalid form data, userID is required');\n        }\n\n        // [6] Both user-controlled values passed directly — no ownership or identity checks\n        yield* sdk.REST_API.tokens.delete({ tokenId: jsonData.tokenID, userId: jsonData.userID });\n\n        return apiResponseLogger(200, 'Token deleted');                         // [7]\n    }),\n```\n**Analysis**\nThe handler shares the same class of authorization flaws found in the token generation endpoint, applied to a destructive operation:\n1. **Insufficient permission gate [1][2][3]:** The handler retrieves the session from ctx.locals.StudioCMS.security and only checks isEditor. Token revocation is a high-privilege operation that should require ownership of the token or elevated administrative privileges — not a generic editor-level gate.\n2. **No token ownership validation [4][6]:** The handler does not verify that jsonData.tokenID actually belongs to the jsonData.userID supplied in the payload. An attacker could enumerate or guess token IDs and revoke them regardless of ownership.\n3. **Missing caller identity check [5][6]:** The jsonData.userID from the payload is never compared against userData (the authenticated caller from [1]). Any editor can specify an arbitrary target user UUID and revoke their tokens.\n4. **No role hierarchy enforcement [6]:** There is no check preventing a lower-privileged user (editor) from revoking tokens belonging to higher-privileged accounts (admin, owner).\n5. **Direct pass-through to destructive operation [6][7]:** Both user-controlled parameters are passed directly to sdk.REST_API.tokens.delete() without any server-side validation, and the server responds with a generic success message, making this a textbook IDOR.\n\n## PoC\n**Environment**\n*User ID | Role*\n2450bf33-0135-4142-80be-9854f9a5e9f1 | owner\n39b3e7d3-5eb0-48e1-abdc-ce95a57b212c | editor\n\n**Attack — Editor Revokes Owner's API Token**\nAn authenticated editor sends the following request to revoke a token belonging to the owner:\n```\nDELETE /studiocms_api/dashboard/api-tokens HTTP/1.1\nHost: 127.0.0.1:4321\nCookie: auth_session=<editor_session_cookie>\nContent-Type: application/json\nAccept: application/json\nContent-Length: 98\n\n{\n  \"tokenID\": \"16a2e549-513b-40ac-8ca3-858af6118afc\",\n  \"userID\": \"2450bf33-0135-4142-80be-9854f9a5e9f1\"\n}\n```\n\n**Response (HTTP 200):**\n```\n{\"message\":\"Token deleted\"}\n```\nThe server confirmed deletion of the owner's token. The tokenID here refers to the internal token record identifier (UUID), not the JWT value itself. The editor's session cookie was sufficient to authorize this destructive action against a higher-privileged user.\n\n## Impact\n- **Denial of Service on integrations:** API tokens used in CI/CD pipelines, third-party integrations, or monitoring systems can be silently revoked, causing automated workflows to fail without warning.\n- **No audit trail:** The revocation is processed as a legitimate operation — the only evidence is the editor's own session, making attribution difficult without detailed request logging.",
                    "title": "github - https://api.github.com/advisories/GHSA-8rgj-vrfr-6hqr"
                },
                {
                    "category": "other",
                    "text": "0.00041",
                    "title": "EPSS"
                },
                {
                    "category": "other",
                    "text": "3.6",
                    "title": "NCSC Score"
                },
                {
                    "category": "other",
                    "text": "Is related to CWE-863 (Incorrect Authorization)",
                    "title": "NCSC Score top increasing factors"
                },
                {
                    "category": "other",
                    "text": "There is exploit data available from source Nvd, Exploit code publicly available",
                    "title": "NCSC Score top decreasing factors"
                }
            ],
            "product_status": {
                "known_affected": [
                    "CSAFPID-5775284",
                    "CSAFPID-5837993"
                ]
            },
            "references": [
                {
                    "category": "external",
                    "summary": "Source - cveprojectv5",
                    "url": "https://www.cve.org/CVERecord?id=CVE-2026-30945"
                },
                {
                    "category": "external",
                    "summary": "Source raw - cveprojectv5",
                    "url": "https://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/30xxx/CVE-2026-30945.json"
                },
                {
                    "category": "external",
                    "summary": "Source - nvd",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30945"
                },
                {
                    "category": "external",
                    "summary": "Source raw - nvd",
                    "url": "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-30945"
                },
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://github.com/advisories/GHSA-8rgj-vrfr-6hqr"
                },
                {
                    "category": "external",
                    "summary": "Source raw - github",
                    "url": "https://api.github.com/advisories/GHSA-8rgj-vrfr-6hqr"
                },
                {
                    "category": "external",
                    "summary": "Source - first",
                    "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-30945"
                },
                {
                    "category": "external",
                    "summary": "Source raw - first",
                    "url": "https://api.first.org/data/v1/epss?limit=10000&offset=0"
                },
                {
                    "category": "external",
                    "summary": "Source - github",
                    "url": "https://api.github.com/advisories/GHSA-8rgj-vrfr-6hqr"
                },
                {
                    "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-8rgj-vrfr-6hqr"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd",
                    "url": "https://github.com/withstudiocms/studiocms/commit/9eec9c3b45523b635cfe16d55aa55afabacbebe3"
                },
                {
                    "category": "external",
                    "summary": "Reference - cveprojectv5; github; nvd",
                    "url": "https://github.com/withstudiocms/studiocms/releases/tag/studiocms@0.4.0"
                },
                {
                    "category": "external",
                    "summary": "Reference - github",
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30945"
                },
                {
                    "category": "external",
                    "summary": "Reference - github",
                    "url": "https://github.com/advisories/GHSA-8rgj-vrfr-6hqr"
                }
            ],
            "scores": [
                {
                    "cvss_v3": {
                        "version": "3.1",
                        "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H",
                        "baseScore": 7.1,
                        "baseSeverity": "HIGH"
                    },
                    "products": [
                        "CSAFPID-5775284",
                        "CSAFPID-5837993"
                    ]
                }
            ],
            "title": "CVE-2026-30945"
        }
    ]
}