GitHub Security Advisory: GHSA-5hxf-c7j4-279c
Package: tinacms (NPM)
Severity: HIGH (CVSS 7.4)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Vulnerable versions: < 2.1.7
Patched in: 2.1.7

Summary: Tina: Path Traversal in Media Upload Handle

Details:
## 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 |

---

## Summary

A **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 Execution

---

## Details

### Vulnerable Code Location

**File**: `packages/@tinacms/cli/src/next/commands/dev-command/server/media.ts`  
**Lines**: 42-43

```typescript
bb.on('file', async (_name, file, _info) => {
  const fullPath = decodeURI(req.url?.slice('/media/upload/'.length));  // Line 42
  const saveTo = path.join(mediaFolder, ...fullPath.split('/'));        // Line 43
  // make sure the directory exists before writing the file
  await fs.ensureDir(path.dirname(saveTo));
  file.pipe(fs.createWriteStream(saveTo));
});
```

### Root Cause

The `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**:
```javascript
const mediaFolder = '/app/public/uploads';
const maliciousInput = '../../../tmp/evil.txt';
const saveTo = path.join(mediaFolder, ...maliciousInput.split('/'));
// Result: '/tmp/evil.txt' - OUTSIDE the media folder!
```

### Additional Affected Endpoints

The same vulnerability pattern exists in:

1. **Delete Handler** (`handleDelete`, lines 29-33) - Arbitrary file deletion
2. **List Handler** (`handleList`, lines 16-27) + `MediaModel.listMedia` - Directory enumeration
3. **MediaModel.deleteMedia** (lines 201-217) - Arbitrary file deletion

Similar code also exists in the Express version at:
- `packages/@tinacms/cli/src/server/routes/index.ts`
- `packages/@tinacms/cli/src/server/models/media.ts`

---

## PoC

### Quick Verification (No Server Required)

This Node.js script directly tests the vulnerable code logic:

```javascript
#!/usr/bin/env node
/**
 * TinaCMS Path Traversal Vulnerability - Direct Code Test
 * Run: node test-vulnerability.js
 */

const path = require('path');
const fs = require('fs');

// Simulated configuration (matches typical TinaCMS setup)
const rootPath = '/tmp/tinacms-test';
const publicFolder = 'public';
const mediaRoot = 'uploads';
const mediaFolder = path.join(rootPath, publicFolder, mediaRoot);

// Setup test directories
fs.mkdirSync(path.join(rootPath, publicFolder, mediaRoot), { recursive: true });
fs.mkdirSync('/tmp/target-dir', { recursive: true }