GHSA-RW26-WWFC-9WVM
Vulnerability from github – Published: 2026-10-21 17:13 – Updated: 2026-10-21 17:131. Summary
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.
2. Affected Files & Endpoints
- Endpoint:
POST /api/v1/@apostrophecms/import-export/clean-export - Route Definition:
packages/import-export/lib/apiRoutes.js(lines 32–34) - Method Implementation:
packages/import-export/lib/methods/import.js(lines 1271–1324)
3. Root Cause Analysis
In packages/import-export/lib/apiRoutes.js, the get.related handler explicitly validates that the caller is logged in:
get: {
async related(req) {
if (!req.user) {
throw self.apos.error('forbidden');
}
// ...
}
}
In contrast, post.cleanExport (as well as post.overrideDuplicates) lacks any verification of req.user:
post: {
async overrideDuplicates(req) {
return self.overrideDuplicates(req);
},
async cleanExport(req) {
return self.cleanExport(req); // <-- No authentication check
}
}
Because 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.
4. Evidence
When 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:
at Object.cleanExport (.../packages/import-export/lib/methods/import.js)
at .../packages/apostrophe/modules/@apostrophecms/module/index.js:193:30
This confirms that execution directly reaches the core import-export handler without an authenticated session.
5. Impact
- Broken Access Control: Unauthenticated users can invoke cleanup operations and delete temporary export files and artifacts stored on the server.
- Unauthorized State Mutation: The endpoint allows modifying job records in the database via
req.body.jobId(forcing them toended: true, status: 'completed') and dismissing notifications viareq.body.notificationIdwithout verifying whether the resources belong to the requesting caller.
6. Suggested Remediation (Patch)
Enforce an authentication check in packages/import-export/lib/apiRoutes.js before executing the underlying methods:
--- a/packages/import-export/lib/apiRoutes.js
+++ b/packages/import-export/lib/apiRoutes.js
@@ -26,10 +26,16 @@ module.exports = self => {
get: { ... },
post: {
async overrideDuplicates(req) {
+ if (!req.user) {
+ throw self.apos.error('forbidden');
+ }
return self.overrideDuplicates(req);
},
async cleanExport(req) {
+ if (!req.user) {
+ throw self.apos.error('forbidden');
+ }
return self.cleanExport(req);
}
}
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@apostrophecms/import-export"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.7.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-113648"
],
"database_specific": {
"cwe_ids": [
"CWE-306",
"CWE-862"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-21T17:13:28Z",
"nvd_published_at": "2026-10-20T17:17:35Z",
"severity": "LOW"
},
"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\u2014including unauthenticated anonymous users who have obtained the standard CSRF cookie from visiting the site\u2014can 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\u201334)\n* **Method Implementation:** `packages/import-export/lib/methods/import.js` (lines 1271\u20131324)\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(\u0027forbidden\u0027);\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\u2019s 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: \u0027completed\u0027`) 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(\u0027forbidden\u0027);\n+ }\n return self.overrideDuplicates(req);\n },\n \n async cleanExport(req) {\n+ if (!req.user) {\n+ throw self.apos.error(\u0027forbidden\u0027);\n+ }\n return self.cleanExport(req);\n }\n }\n```",
"id": "GHSA-rw26-wwfc-9wvm",
"modified": "2026-10-21T17:13:28Z",
"published": "2026-10-21T17:13:28Z",
"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"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "ApostropheCMS: Missing Authentication in `cleanExport` Route"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.