{"schema":"threatline.record/2","record":{"id":"CVE-2026-113648","aliases":["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); // <-- 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 => {\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```","categories":["Libraries & packages"],"product":"@apostrophecms/import-export","vendor":"apostrophecms","ecosystem":"npm","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-rw26-wwfc-9wvm","source_modified_at":"2026-10-21T17:13:28Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@apostrophecms/import-export"},"range":"< 3.7.1"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-113648","source_modified_at":"2026-10-23T01:52:13.055Z","fetched_at":"2026-10-23T18:42:43.628194Z","product":"apostrophe","vendor":"apostrophecms","versions":[{"version":"< 4.34.0","status":"affected"}],"defaultStatus":null},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-113648","source_modified_at":"2026-10-23T01:52:13.055Z","fetched_at":"2026-10-23T18:42:43.628194Z","product":"import-export","vendor":"@apostrophecms","versions":[{"version":"< 3.7.1","status":"affected"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-rw26-wwfc-9wvm","source_modified_at":"2026-10-21T17:28:21.930781411Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@apostrophecms/import-export","ecosystem":"npm","purl":"pkg:npm/%40apostrophecms/import-export"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.7.1"}]}],"versions":[]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-rw26-wwfc-9wvm","source_modified_at":"2026-10-21T17:13:28Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"npm","name":"@apostrophecms/import-export"},"version":"3.7.1"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-rw26-wwfc-9wvm","source_modified_at":"2026-10-21T17:28:21.930781411Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"name":"@apostrophecms/import-export","ecosystem":"npm","purl":"pkg:npm/%40apostrophecms/import-export"},"version":"3.7.1"}],"cvss":3.1,"severity":"Low","kev":{"listed":false},"epss":{"probability":0.00328,"percentile":0.24669,"score_date":"2026-10-23"},"published_at":"2026-10-20T16:07:49.138Z","modified_at":"2026-10-23T01:52:13.055Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-rw26-wwfc-9wvm","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-21T17:13:28Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-113648","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23T01:52:13.055Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-rw26-wwfc-9wvm","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-21T17:28:21.930781411Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-113648","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-rw26-wwfc-9wvm","tags":["x_refsource_CONFIRM"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-113648","source":"GitHub Advisory Database"},{"source":"CVE List","url":"https://github.com/apostrophecms/apostrophe/commit/06e4b377f3c360f9e89c32dccd7e505ad5ddf498","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/apostrophecms/apostrophe/commit/40b989b1a9dd23d30029b8ce7668634265cfe5ea","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/apostrophecms/apostrophe/releases/tag/@apostrophecms/import-export@3.7.1","tags":["x_refsource_MISC"]},{"url":"https://github.com/advisories/GHSA-rw26-wwfc-9wvm","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"none","automatable":"no","technical_impact":"partial","assessed_at":"2026-10-23T01:52:01.409774Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-23T01:52:13.055Z","content_hash":"1c7195a146d0a931d2b5b4968f1c43f571d1f4bdc6eca0a0a98e102d1a1c3a80"}}
