{"id":"CVE-2026-113648","aliases":["EUVD-2026-105172","GHSA-rw26-wwfc-9wvm"],"status":"published","title":"ApostropheCMS: Missing Authentication in `cleanExport` Route","description":"ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, the 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. This issue is fixed in version 3.7.1.","severity":{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N","level":"low","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00328,"percentile":0.26261,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-306","CWE-862"],"published":"2026-10-20T16:07:49Z","modified":"2026-10-28T02:31:05Z","fixes":[{"ecosystem":"npm","package":"@apostrophecms/import-export","fixed_in":["3.7.1"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"@apostrophecms/import-export","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.7.1"}]}]},{"source":"osv","ecosystem":"npm","package":"@apostrophecms/import-export","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.7.1"}]}]}],"products":[{"vendor":"@apostrophecms","product":"import-export"},{"vendor":"apostrophecms","product":"apostrophe"},{"vendor":"apostrophecms","product":"import-export"}],"references":[{"url":"https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-rw26-wwfc-9wvm","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/apostrophecms/apostrophe/commit/06e4b377f3c360f9e89c32dccd7e505ad5ddf498","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/apostrophecms/apostrophe/commit/40b989b1a9dd23d30029b8ce7668634265cfe5ea","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/apostrophecms/apostrophe/releases/tag/@apostrophecms/import-export@3.7.1","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-113648","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-rw26-wwfc-9wvm"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/113xxx/CVE-2026-113648.json","tags":["advisory"]},{"url":"https://github.com/apostrophecms/apostrophe","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-113648","title":"ApostropheCMS: Missing Authentication in `cleanExport` Route","description":"ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, the 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. This issue is fixed in version 3.7.1.","status":"published","published":"2026-10-20T16:07:49.138Z","modified":"2026-10-23T01:52:13.055Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N","level":"low","assessor":"cna"}],"cwes":["CWE-306","CWE-862"],"updated_at":"2026-11-02T13:44:29.519518Z"}],"euvd":[{"id":"EUVD-2026-105172","description":"ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, the 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. This issue is fixed in version 3.7.1.","status":"published","published":"2026-10-20T16:07:49Z","modified":"2026-10-23T01:52:13Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N","level":"low"}],"updated_at":"2026-11-02T16:59:33.524592Z"}],"ghsa":[{"id":"GHSA-rw26-wwfc-9wvm","title":"ApostropheCMS: Missing Authentication in `cleanExport` Route","description":"#### 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 \u0026 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```","status":"published","published":"2026-10-21T17:13:28Z","modified":"2026-10-21T17:13:28Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N","level":"low"}],"cwes":["CWE-306","CWE-862"],"updated_at":"2026-11-02T14:41:34.690842Z"}],"nvd":[{"id":"CVE-2026-113648","description":"ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, the 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. This issue is fixed in version 3.7.1.","status":"published","published":"2026-10-20T17:17:35.537Z","modified":"2026-10-23T02:16:55.923Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N","level":"low","assessor":"secondary"}],"cwes":["CWE-306","CWE-862"],"updated_at":"2026-11-02T14:13:01.161484Z"}],"osv":[{"id":"CVE-2026-113648","title":"ApostropheCMS: Missing Authentication in `cleanExport` Route","description":"ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, the 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. This issue is fixed in version 3.7.1.","status":"published","published":"2026-10-20T16:07:49.138Z","modified":"2026-10-28T02:31:05.23620655Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N","level":"low"}],"cwes":["CWE-306","CWE-862"],"updated_at":"2026-11-02T15:44:56.310988Z"},{"id":"GHSA-rw26-wwfc-9wvm","title":"ApostropheCMS: Missing Authentication in `cleanExport` Route","description":"#### 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 \u0026 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```","status":"published","published":"2026-10-21T17:13:28Z","modified":"2026-10-21T17:28:21.930781411Z","cvss":[{"version":"3.1","score":3.1,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N","level":"low"}],"cwes":["CWE-306","CWE-862"],"updated_at":"2026-11-02T15:46:56.281047Z"}]}}
