{"id":"GHSA-rw26-wwfc-9wvm","summary":"ApostropheCMS: Missing Authentication in `cleanExport` Route","details":"#### 1. Summary\nThe API endpoint `POST /api/v1/@apostrophecms/import-export/clean-export` in `@apostrophecms/import-export` lacks authentication and authorization checks. Any client—including unauthenticated anonymous users who have obtained the standard CSRF cookie from visiting the site—can directly invoke this route and trigger the underlying `self.cleanExport(req)` method.\n\n---\n\n#### 2. Affected Files & Endpoints\n* **Endpoint:** `POST /api/v1/@apostrophecms/import-export/clean-export`\n* **Route Definition:** `packages/import-export/lib/apiRoutes.js` (lines 32–34)\n* **Method Implementation:** `packages/import-export/lib/methods/import.js` (lines 1271–1324)\n\n---\n\n#### 3. Root Cause Analysis\nIn `packages/import-export/lib/apiRoutes.js`, the `get.related` handler explicitly validates that the caller is logged in:\n```javascript\nget: {\n  async related(req) {\n    if (!req.user) {\n      throw self.apos.error('forbidden');\n    }\n    // ...\n  }\n}\n```\nIn contrast, `post.cleanExport` (as well as `post.overrideDuplicates`) lacks any verification of `req.user`:\n```javascript\npost: {\n  async overrideDuplicates(req) {\n    return self.overrideDuplicates(req);\n  },\n\n  async cleanExport(req) {\n    return self.cleanExport(req); // \u003c-- No authentication check\n  }\n}\n```\nBecause ApostropheCMS does not enforce authentication globally on all `apiRoutes`, omitting an explicit `if (!req.user)` guard allows anonymous requests to pass directly through to the business logic layer.\n\n---\n\n#### 4. Evidence\nWhen sending an unauthenticated POST request with the site’s CSRF cookie (issued to any visitor on `GET /`), the request bypasses access control entirely and executes `self.cleanExport(req)`, as demonstrated by the server stack trace:\n\n```text\nat Object.cleanExport (.../packages/import-export/lib/methods/import.js)\nat .../packages/apostrophe/modules/@apostrophecms/module/index.js:193:30\n```\nThis confirms that execution directly reaches the core import-export handler without an authenticated session.\n\n---\n\n#### 5. Impact\n1. **Broken Access Control:** Unauthenticated users can invoke cleanup operations and delete temporary export files and artifacts stored on the server.\n2. **Unauthorized State Mutation:** The endpoint allows modifying job records in the database via `req.body.jobId` (forcing them to `ended: true, status: 'completed'`) and dismissing notifications via `req.body.notificationId` without verifying whether the resources belong to the requesting caller.\n\n---\n\n#### 6. Suggested Remediation (Patch)\n\nEnforce an authentication check in `packages/import-export/lib/apiRoutes.js` before executing the underlying methods:\n\n```diff\n--- a/packages/import-export/lib/apiRoutes.js\n+++ b/packages/import-export/lib/apiRoutes.js\n@@ -26,10 +26,16 @@ module.exports = self =\u003e {\n     get: { ... },\n     post: {\n       async overrideDuplicates(req) {\n+        if (!req.user) {\n+          throw self.apos.error('forbidden');\n+        }\n         return self.overrideDuplicates(req);\n       },\n \n       async cleanExport(req) {\n+        if (!req.user) {\n+          throw self.apos.error('forbidden');\n+        }\n         return self.cleanExport(req);\n       }\n     }\n```","aliases":["CVE-2026-113648"],"modified":"2026-10-21T17:28:21.930781411Z","published":"2026-10-21T17:13:28Z","database_specific":{"github_reviewed_at":"2026-10-21T17:13:28Z","nvd_published_at":"2026-10-20T17:17:35Z","cwe_ids":["CWE-306","CWE-862"],"severity":"LOW","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-rw26-wwfc-9wvm"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-113648"},{"type":"WEB","url":"https://github.com/apostrophecms/apostrophe/commit/06e4b377f3c360f9e89c32dccd7e505ad5ddf498"},{"type":"WEB","url":"https://github.com/apostrophecms/apostrophe/commit/40b989b1a9dd23d30029b8ce7668634265cfe5ea"},{"type":"PACKAGE","url":"https://github.com/apostrophecms/apostrophe"},{"type":"WEB","url":"https://github.com/apostrophecms/apostrophe/releases/tag/@apostrophecms/import-export@3.7.1"}],"affected":[{"package":{"name":"@apostrophecms/import-export","ecosystem":"npm","purl":"pkg:npm/%40apostrophecms/import-export"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.7.1"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-rw26-wwfc-9wvm/GHSA-rw26-wwfc-9wvm.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N"}]}