GHSA-5hxf-c7j4-279c Tina: Path Traversal in Media Upload Handle
Affected Package| Field | Value |
|-------|-------|
| **Package** | `@tinacms/cli` |
| **Version** | `2.0.5` (latest at time of discovery) |
| **Vulnerable File** | `packages/@tinacms/cli/src/next/commands/dev-command/server/media.ts` |
| **Vulnerable Lines** | 42-43 |SummaryA **path traversal vulnerability (CWE-22)** exists in the TinaCMS development server's media upload handler. The code at `media.ts:42-43` joins user-controlled path segments using `path.join()` without validating that the resulting path stays within the intended media directory. This allows writing files to arbitrary locations on the filesystem.**Attack Vector**: Network (HTTP POST request)  
**Impact**: Arbitrary file write, potential Remote Code ExecutionDetailsVulnerable Code Location**File**: `packages/@tinacms/cli/src/next/commands/dev-command/server/media.ts`  
**Lines**: 42-43Root CauseThe `path.join()` function resolves `..` (parent directory) segments in the path. When the user-supplied path contains traversal sequences like `../../../etc/passwd`, these are resolved relative to the media folder, allowing escape to arbitrary filesystem locations.**Example**:Additional Affected EndpointsThe same vulnerability pattern exists in:**Delete Handler** (`handleDelete`, lines 29-33) - Arbitrary file deletion**List Handler** (`handleList`, lines 16-27) + `MediaModel.listMedia` - Directory enumeration**MediaModel.deleteMedia** (lines 201-217) - Arbitrary file deletionSimilar code also exists in the Express version at:`packages/@tinacms/cli/src/server/routes/index.ts``packages/@tinacms/cli/src/server/models/media.ts`PoCQuick Verification (No Server Required)This Node.js script directly tests the vulnerable code logic:OutputThe file was successfully written to `/tmp/tmp/target-dir/evil.txt`, which is **completely outside** the intended media folder at `/tmp/tinacms-test/public/uploads`.Important Note: HTTP Layer vs Code VulnerabilityI want to be transparent about my findings:**What I observed:**When testing via HTTP requests against the Vite dev server, path traversal sequences (`../`) are normalized by Node.js/Vite's HTTP layer *before* reaching the vulnerable codeThis means direct HTTP exploitation like `curl POST /media/upload/../../../tmp/evil.txt` is mitigated in the default configuration**Why this is still a valid vulnerability that should be fixed:****The code itself has no validation** - If the path reaches the handler (via any vector), it will be exploited**Defense-in-depth principle** - Security should not rely solely on HTTP normalization**Inconsistent protection** - Your GraphQL layer (`addPendingDocument`) explicitly validates paths and rejects `../` (see test at `packages/@tinacms/graphql/tests/pending-document-validation/index.test.ts:59`), but the media endpoints don't have equivalent protection**Different deployment contexts**:Reverse proxies (nginx, Apache) with `proxy_pass` may preserve raw pathsCustom server configurationsFuture refactoring that uses this code differently**The `parseMediaFolder` helper** (line 66-74) shows intent to restrict paths - the upload handler should have similar restrictions**Express version also affected** - `packages/@tinacms/cli/src/server/routes/index.ts` has the same patternEvidence That Path Traversal Should Be BlockedYour codebase already shows that path traversal is considered a security issue:This test explicitly verifies that `../invalid-path.md` is rejected in the GraphQL layer. The media upload endpoints should have the same protection.ImpactWho is AffectedDevelopers running TinaCMS in development modeAny deployment exposing the TinaCMS dev server APIParticularly concerning if dev servers are exposed to networks (common for mobile testing)Potential Attack Scenarios**Remote Code Execution**: Write malicious files to executable locationsOverwrite `~/.ssh/authorized_keys` for SSH accessModify application source codeCreate cron jobs or systemd services**Denial of Service**: Delete critical application or system files**Information Disclosure**: List directory contents outside the media folderCVSS Score Estimate**CVSS 3.1 Base Score: 8.1 (High)**Attack Vector: Network (AV:N)Attack Complexity: Low (AC:L)Privileges Required: None (PR:N)User Interaction: None (UI:N)Scope: Unchanged (S:U)Confidentiality: None (C:N)Integrity: High (I:H)Availability: High (A:H)Recommended FixAdd path validation to ensure the resolved path stays within the media directory:The same fix should be applied to:`handleDelete` function`handleList` function`MediaModel.listMedia` method`MediaModel.deleteMedia` methodExpress router in `packages/@tinacms/cli/src/server/`Alternative: Create a Validation HelperReferences[CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')](https://cwe.mitre.org/data/definitions/22.html)[OWASP Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)[Node.js path.join() Documentation](https://nodejs.org/api/path.html#pathjoinpaths)[OWASP Testing Guide - Path Traversal](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/05-Authorization_Testing/01-Testing_Directory_Traversal_File_Include)
CVSS v3.1 score: 7.4