Saltcorn has an Unauthenticated Path Traversal in sync endpoints, allowing arbitrary file write and directory read
Date: 2026-04
Affected: npm/@saltcorn/server, npm/@saltcorn/server, npm/@saltcorn/server
Attack vector: path-traversal
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-40163
CWE: CWE-22
CVSS: 8.2 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N)

### Summary Two unauthenticated path traversal vulnerabilities exist in Saltcorn's mobile sync endpoints. The `POST /sync/offline_changes` endpoint allows an unauthenticated attacker to create arbitrary directories and write a `changes.json` file with attacker-controlled JSON content anywhere on the server filesystem. The `GET /sync/upload_finished` endpoint allows an unauthenticated attacker to list arbitrary directory contents and read specific JSON files. The safe path validation function `File.normalise_in_base()` exists in the codebase and is correctly used by the `clean_sync_dir` endpoint in the **same file** (fix for GHSA-43f3-h63w-p6f6), but was not applied to these two endpoints. ### Details **Finding 1: Arbitrary file write — `POST /sync/offline_changes` (sync.js line 226)** The `newSyncTimestamp` parameter from the request body is used directly in `path.join()` without sanitization: ```javascript const syncDirName = `${newSyncTimestamp}_${req.user?.email || "public"}`; const syncDir = path.join( rootFolder.location, "mobile_app", "sync", syncDirName ); await fs.mkdir(syncDir, { recursive: true }); // creates arbitrary dir await fs.writeFile( path.join(syncDir, "changes.json"), JSON.stringify(changes) // writes attacker content ); ``` No authentication middleware is applied to this route. Since `path.join()` normalizes `../` sequences, setting `newSyncTimestamp` to `../../../../tmp/evil` causes the path to resolve


Mitigations:

Architectural layers (MAESTRO):