GitHub Security Advisory: GHSA-mwxc-m426-3f78
Package: @apostrophecms/import-export (NPM)
Severity: CRITICAL (CVSS 9.9)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Vulnerable versions: <= 3.5.2
Patched in: 3.5.3

Summary: ApostropheCMS has Arbitrary File Write (Zip Slip / Path Traversal) in Import-Export Gzip Extraction

Details:
**Reported:** 2026-03-08  
**Status:** patched and released in version 3.5.3 of `@apostrophecms/import-export`

---

## Product

| Field | Value |
|---|---|
| Repository | `apostrophecms/apostrophe` (monorepo) |
| Affected Package | `@apostrophecms/import-export` |
| Affected File | `packages/import-export/lib/formats/gzip.js` |
| Affected Function | `extract(filepath, exportPath)` — lines ~132–157 |
| Minimum Required Permission | **Global Content Modify** (any editor-level user with import access) |

---

## Vulnerability Summary

The `extract()` function in `gzip.js` constructs file-write paths using:

```js
fs.createWriteStream(path.join(exportPath, header.name))
```

`path.join()` does **not** resolve or sanitise traversal segments such as `../`. It concatenates them as-is, meaning a tar entry named `../../evil.js` resolves to a path **outside** the intended extraction directory. No canonical-path check is performed before the write stream is opened.

This is a textbook **Zip Slip** vulnerability. Any user who has been granted the **Global Content Modify** permission — a role routinely assigned to content editors and site managers — can upload a crafted `.tar.gz` file through the standard CMS import UI and write attacker-controlled content to **any path the Node.js process can reach on the host filesystem**.

---

## Security Impact

This vulnerability provides **unauthenticated-equivalent arbitrary file write** to any user with content editor permissions. The full impact chain is:

### 1. Arbitrary File Write
Write any file to any path the Node.js process user can access. Confirmed writable targets in testing:

- Any path the CMS process has permission to

### 2. Static Web Directory — Defacement & Malicious Asset Injection
ApostropheCMS serves `<project-root>/public/` via Express static middleware:

```js
// packages/apostrophe/modules/@apostrophecms/asset/index.js
express.static(self.apos.rootDir + '/public', self.options.static || {})
```

A traversal payload targeting `public/` makes any uploaded file **directly HTTP-accessible**:

This enables:
- Full site defacement
- Serving phishing pages from the legitimate CMS domain
- Injecting malicious JavaScript served to all site visitors (stored XSS at scale)

### 3. Persistent Backdoor / RCE (Post-Restart)
If the traversal targets any `.js` file loaded by Node.js on startup (e.g., a module `index.js`, a config file, a routes file), the payload becomes a **persistent backdoor** that executes with the CMS process privileges on the next server restart. In container/cloud environments, restarts happen automatically on deploy, crash, or health-check failure — meaning the attacker does not need to manually trigger one.

### 4. Credential and Secret File Overwrite
Overwrite `.env`, `app.config.js`, database seed files, or any config file to:
- Exfiltrate database credentials on next load
- Redirect authentication to an attacker-controlled backend
- Disable security controls (rate limiting, MFA, CS