CVE-2026-28791: Tina Path Traversal Vulnerability

HIGH
Advisory Identifier: GHSA-5hxf-c7j4-279c
CVSS 7.4
CWE-22
Published
Advisory Summary

CVE-2026-28791 is a HIGH (CVSS 7.4) path traversal vulnerability (CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')) affecting Tina libraries in the npm ecosystem. The issue involves path traversal in media upload handle. Remediation is available by upgrading affected packages to version 2.1.7.

CVE-2026-28791 at a Glance

CVE Identifier CVE-2026-28791
GHSA Identifier GHSA-5hxf-c7j4-279c
Severity HIGH
CVSS v3.1 Base Score 7.4
CWE Classification CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Affected Product Tina
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 7.4

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity High (AC:H)
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)

How to Fix CVE-2026-28791

To remediate this vulnerability, upgrade all affected Tina dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

tinacms fixed: 2.1.7
npm install tinacms@2.1.7
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls tinacms

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-28791, inspect your project dependencies using npm:

npm ls tinacms

Analyze the terminal output to locate any version of Tina in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.1.7), your application is affected.

Note: The npm ls tinacms command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), where the product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. In Tina, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## 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 }); console.log(`Media folder: ${mediaFolder}`); // Simulate vulnerable code from media.ts:42-43 function vulnerableUpload(reqUrl) { const fullPath = decodeURI(reqUrl.slice('/media/upload/'.length)); const saveTo = path.join(mediaFolder, ...fullPath.split('/')); return saveTo; } // Test cases const tests = [ { url: '/media/upload/image.png', desc: 'Normal upload' }, { url: '/media/upload/../../../tmp/target-dir/evil.txt', desc: 'Path traversal' }, ]; tests.forEach(test => { const result = vulnerableUpload(test.url); const isVuln = !path.resolve(result).startsWith(path.resolve(mediaFolder)); console.log(`\n${test.desc}:`); console.log(` Input: ${test.url}`); console.log(` Result: ${result}`); console.log(` Vulnerable: ${isVuln ? 'YES ⚠️' : 'No ✓'}`); if (isVuln) { // Actually write the file to prove it works fs.mkdirSync(path.dirname(result), { recursive: true }); fs.writeFileSync(result, `PWNED at ${new Date().toISOString()}`); console.log(` File written: ${fs.existsSync(result)}`); } }); // Cleanup fs.rmSync(rootPath, { recursive: true, force: true }); ``` ### Output ``` Media folder: /tmp/tinacms-test/public/uploads Normal upload: Input: /media/upload/image.png Result: /tmp/tinacms-test/public/uploads/image.png Vulnerable: No ✓ Path traversal: Input: /media/upload/../../../tmp/target-dir/evil.txt Result: /tmp/tmp/target-dir/evil.txt Vulnerable: YES ⚠️ File written: true ``` The 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 Vulnerability I 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 code - This 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:** 1. **The code itself has no validation** - If the path reaches the handler (via any vector), it will be exploited 2. **Defense-in-depth principle** - Security should not rely solely on HTTP normalization 3. **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 4. **Different deployment contexts**: - Reverse proxies (nginx, Apache) with `proxy_pass` may preserve raw paths - Custom server configurations - Future refactoring that uses this code differently 5. **The `parseMediaFolder` helper** (line 66-74) shows intent to restrict paths - the upload handler should have similar restrictions 6. **Express version also affected** - `packages/@tinacms/cli/src/server/routes/index.ts` has the same pattern --- ### Evidence That Path Traversal Should Be Blocked Your codebase already shows that path traversal is considered a security issue: ```typescript // From: packages/@tinacms/graphql/tests/pending-document-validation/index.test.ts:52-70 it('handles validation error for invalid path format', async () => { const { query } = await setupMutation(__dirname, config); const invalidPathMutation = ` mutation { addPendingDocument( collection: "post" relativePath: "../invalid-path.md" // <-- Path traversal is rejected! ) { __typename } } `; const result = await query({ query: invalidPathMutation, variables: {} }); expect(result.errors).toBeDefined(); expect(result.errors?.length).toBeGreaterThan(0); }); ``` This test explicitly verifies that `../invalid-path.md` is rejected in the GraphQL layer. The media upload endpoints should have the same protection. --- ## Impact ### Who is Affected - Developers running TinaCMS in development mode - Any deployment exposing the TinaCMS dev server API - Particularly concerning if dev servers are exposed to networks (common for mobile testing) ### Potential Attack Scenarios 1. **Remote Code Execution**: Write malicious files to executable locations - Overwrite `~/.ssh/authorized_keys` for SSH access - Modify application source code - Create cron jobs or systemd services 2. **Denial of Service**: Delete critical application or system files 3. **Information Disclosure**: List directory contents outside the media folder ### CVSS 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 Fix Add path validation to ensure the resolved path stays within the media directory: ```typescript import path from 'path'; const handlePost = async function (req, res) { const bb = busboy({ headers: req.headers }); bb.on('file', async (_name, file, _info) => { const fullPath = decodeURI(req.url?.slice('/media/upload/'.length)); const saveTo = path.join(mediaFolder, ...fullPath.split('/')); // ✅ SECURITY FIX: Validate path stays within media folder const resolvedPath = path.resolve(saveTo); const resolvedMediaFolder = path.resolve(mediaFolder); if (!resolvedPath.startsWith(resolvedMediaFolder + path.sep)) { res.statusCode = 403; res.end(JSON.stringify({ error: 'Invalid file path' })); return; } await fs.ensureDir(path.dirname(saveTo)); file.pipe(fs.createWriteStream(saveTo)); }); // ... rest of handler }; ``` The same fix should be applied to: - `handleDelete` function - `handleList` function - `MediaModel.listMedia` method - `MediaModel.deleteMedia` method - Express router in `packages/@tinacms/cli/src/server/` ### Alternative: Create a Validation Helper ```typescript function validateMediaPath(userPath: string, mediaFolder: string): string { const resolved = path.resolve(path.join(mediaFolder, ...userPath.split('/'))); const resolvedBase = path.resolve(mediaFolder); if (!resolved.startsWith(resolvedBase + path.sep) && resolved !== resolvedBase) { throw new Error('Path traversal detected'); } return resolved; } ``` --- ## References - [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)

CVE-2026-28791 Affected Tina Versions

1 component(s)
Fixed Versions:
2.1.7
Product / Package Ecosystem Affected Versions Fixed Version
tinacms
npm
< 2.1.7
2.1.7

References

Frequently Asked Questions

What is CVE-2026-28791?
CVE-2026-28791 is a security vulnerability in Tina involving Path Traversal in Media Upload Handle under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
Which Tina versions are affected?
Affected versions include releases prior to 2.1.7 across affected packages: tinacms.
What version fixes CVE-2026-28791?
Upgrading to 2.1.7 (or the latest patched release for your respective branch) resolves the vulnerability.
Is tinacms affected?
Yes, tinacms is affected in releases prior to 2.1.7. Upgrade your dependency to 2.1.7 or later.
What is the CVSS score of CVE-2026-28791?
CVE-2026-28791 has a CVSS v3.1 base score of 7.4 (HIGH) with vector string CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H.
What CWE is associated with CVE-2026-28791?
CVE-2026-28791 is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.).
How can I check npm dependencies for CVE-2026-28791?
Run the dependency tree command: 'npm ls tinacms'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-5hxf-c7j4-279c",
  "modified" : "2026-03-12T18:32:38Z",
  "published" : "2026-03-12T18:32:38Z",
  "aliases" : [ "CVE-2026-28791" ],
  "summary" : "Tina: Path Traversal in Media Upload Handle",
  "details" : "## Affected Package\n\n| Field | Value |\n|-------|-------|\n| **Package** | `@tinacms/cli` |\n| **Version** | `2.0.5` (latest at time of discovery) |\n| **Vulnerable File** | `packages/@tinacms/cli/src/next/commands/dev-command/server/media.ts` |\n| **Vulnerable Lines** | 42-43 |\n\n---\n\n## Summary\n\nA **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.\n\n**Attack Vector**: Network (HTTP POST request)  \n**Impact**: Arbitrary file write, potential Remote Code Execution\n\n---\n\n## Details\n\n### Vulnerable Code Location\n\n**File**: `packages/@tinacms/cli/src/next/commands/dev-command/server/media.ts`  \n**Lines**: 42-43\n\n```typescript\nbb.on('file', async (_name, file, _info) => {\n  const fullPath = decodeURI(req.url?.slice('/media/upload/'.length));  // Line 42\n  const saveTo = path.join(mediaFolder, ...fullPath.split('/'));        // Line 43\n  // make sure the directory exists before writing the file\n  await fs.ensureDir(path.dirname(saveTo));\n  file.pipe(fs.createWriteStream(saveTo));\n});\n```\n\n### Root Cause\n\nThe `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.\n\n**Example**:\n```javascript\nconst mediaFolder = '/app/public/uploads';\nconst maliciousInput = '../../../tmp/evil.txt';\nconst saveTo = path.join(mediaFolder, ...maliciousInput.split('/'));\n// Result: '/tmp/evil.txt' - OUTSIDE the media folder!\n```\n\n### Additional Affected Endpoints\n\nThe same vulnerability pattern exists in:\n\n1. **Delete Handler** (`handleDelete`, lines 29-33) - Arbitrary file deletion\n2. **List Handler** (`handleList`, lines 16-27) + `MediaModel.listMedia` - Directory enumeration\n3. **MediaModel.deleteMedia** (lines 201-217) - Arbitrary file deletion\n\nSimilar code also exists in the Express version at:\n- `packages/@tinacms/cli/src/server/routes/index.ts`\n- `packages/@tinacms/cli/src/server/models/media.ts`\n\n---\n\n## PoC\n\n### Quick Verification (No Server Required)\n\nThis Node.js script directly tests the vulnerable code logic:\n\n```javascript\n#!/usr/bin/env node\n/**\n * TinaCMS Path Traversal Vulnerability - Direct Code Test\n * Run: node test-vulnerability.js\n */\n\nconst path = require('path');\nconst fs = require('fs');\n\n// Simulated configuration (matches typical TinaCMS setup)\nconst rootPath = '/tmp/tinacms-test';\nconst publicFolder = 'public';\nconst mediaRoot = 'uploads';\nconst mediaFolder = path.join(rootPath, publicFolder, mediaRoot);\n\n// Setup test directories\nfs.mkdirSync(path.join(rootPath, publicFolder, mediaRoot), { recursive: true });\nfs.mkdirSync('/tmp/target-dir', { recursive: true });\n\nconsole.log(`Media folder: ${mediaFolder}`);\n\n// Simulate vulnerable code from media.ts:42-43\nfunction vulnerableUpload(reqUrl) {\n    const fullPath = decodeURI(reqUrl.slice('/media/upload/'.length));\n    const saveTo = path.join(mediaFolder, ...fullPath.split('/'));\n    return saveTo;\n}\n\n// Test cases\nconst tests = [\n    { url: '/media/upload/image.png', desc: 'Normal upload' },\n    { url: '/media/upload/../../../tmp/target-dir/evil.txt', desc: 'Path traversal' },\n];\n\ntests.forEach(test => {\n    const result = vulnerableUpload(test.url);\n    const isVuln = !path.resolve(result).startsWith(path.resolve(mediaFolder));\n    \n    console.log(`\\n${test.desc}:`);\n    console.log(`  Input: ${test.url}`);\n    console.log(`  Result: ${result}`);\n    console.log(`  Vulnerable: ${isVuln ? 'YES ⚠️' : 'No ✓'}`);\n    \n    if (isVuln) {\n        // Actually write the file to prove it works\n        fs.mkdirSync(path.dirname(result), { recursive: true });\n        fs.writeFileSync(result, `PWNED at ${new Date().toISOString()}`);\n        console.log(`  File written: ${fs.existsSync(result)}`);\n    }\n});\n\n// Cleanup\nfs.rmSync(rootPath, { recursive: true, force: true });\n```\n\n### Output\n\n```\nMedia folder: /tmp/tinacms-test/public/uploads\n\nNormal upload:\n  Input: /media/upload/image.png\n  Result: /tmp/tinacms-test/public/uploads/image.png\n  Vulnerable: No ✓\n\nPath traversal:\n  Input: /media/upload/../../../tmp/target-dir/evil.txt\n  Result: /tmp/tmp/target-dir/evil.txt\n  Vulnerable: YES ⚠️\n  File written: true\n```\n\nThe 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`.\n\n### Important Note: HTTP Layer vs Code Vulnerability\n\nI want to be transparent about my findings:\n\n**What I observed:**\n- 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 code\n- This means direct HTTP exploitation like `curl POST /media/upload/../../../tmp/evil.txt` is mitigated in the default configuration\n\n**Why this is still a valid vulnerability that should be fixed:**\n\n1. **The code itself has no validation** - If the path reaches the handler (via any vector), it will be exploited\n2. **Defense-in-depth principle** - Security should not rely solely on HTTP normalization\n3. **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\n4. **Different deployment contexts**:\n   - Reverse proxies (nginx, Apache) with `proxy_pass` may preserve raw paths\n   - Custom server configurations\n   - Future refactoring that uses this code differently\n5. **The `parseMediaFolder` helper** (line 66-74) shows intent to restrict paths - the upload handler should have similar restrictions\n6. **Express version also affected** - `packages/@tinacms/cli/src/server/routes/index.ts` has the same pattern\n\n---\n\n### Evidence That Path Traversal Should Be Blocked\n\nYour codebase already shows that path traversal is considered a security issue:\n\n```typescript\n// From: packages/@tinacms/graphql/tests/pending-document-validation/index.test.ts:52-70\nit('handles validation error for invalid path format', async () => {\n  const { query } = await setupMutation(__dirname, config);\n\n  const invalidPathMutation = `\n    mutation {\n      addPendingDocument(\n        collection: \"post\"\n        relativePath: \"../invalid-path.md\"  // <-- Path traversal is rejected!\n      ) {\n        __typename\n      }\n    }\n  `;\n\n  const result = await query({ query: invalidPathMutation, variables: {} });\n\n  expect(result.errors).toBeDefined();\n  expect(result.errors?.length).toBeGreaterThan(0);\n});\n```\n\nThis test explicitly verifies that `../invalid-path.md` is rejected in the GraphQL layer. The media upload endpoints should have the same protection.\n\n---\n\n## Impact\n\n### Who is Affected\n\n- Developers running TinaCMS in development mode\n- Any deployment exposing the TinaCMS dev server API\n- Particularly concerning if dev servers are exposed to networks (common for mobile testing)\n\n### Potential Attack Scenarios\n\n1. **Remote Code Execution**: Write malicious files to executable locations\n   - Overwrite `~/.ssh/authorized_keys` for SSH access\n   - Modify application source code\n   - Create cron jobs or systemd services\n\n2. **Denial of Service**: Delete critical application or system files\n\n3. **Information Disclosure**: List directory contents outside the media folder\n\n### CVSS Score Estimate\n\n**CVSS 3.1 Base Score: 8.1 (High)**\n- Attack Vector: Network (AV:N)\n- Attack Complexity: Low (AC:L)  \n- Privileges Required: None (PR:N)\n- User Interaction: None (UI:N)\n- Scope: Unchanged (S:U)\n- Confidentiality: None (C:N)\n- Integrity: High (I:H)\n- Availability: High (A:H)\n\n---\n\n## Recommended Fix\n\nAdd path validation to ensure the resolved path stays within the media directory:\n\n```typescript\nimport path from 'path';\n\nconst handlePost = async function (req, res) {\n  const bb = busboy({ headers: req.headers });\n\n  bb.on('file', async (_name, file, _info) => {\n    const fullPath = decodeURI(req.url?.slice('/media/upload/'.length));\n    const saveTo = path.join(mediaFolder, ...fullPath.split('/'));\n\n    // ✅ SECURITY FIX: Validate path stays within media folder\n    const resolvedPath = path.resolve(saveTo);\n    const resolvedMediaFolder = path.resolve(mediaFolder);\n\n    if (!resolvedPath.startsWith(resolvedMediaFolder + path.sep)) {\n      res.statusCode = 403;\n      res.end(JSON.stringify({ error: 'Invalid file path' }));\n      return;\n    }\n\n    await fs.ensureDir(path.dirname(saveTo));\n    file.pipe(fs.createWriteStream(saveTo));\n  });\n  \n  // ... rest of handler\n};\n```\n\nThe same fix should be applied to:\n- `handleDelete` function\n- `handleList` function  \n- `MediaModel.listMedia` method\n- `MediaModel.deleteMedia` method\n- Express router in `packages/@tinacms/cli/src/server/`\n\n### Alternative: Create a Validation Helper\n\n```typescript\nfunction validateMediaPath(userPath: string, mediaFolder: string): string {\n  const resolved = path.resolve(path.join(mediaFolder, ...userPath.split('/')));\n  const resolvedBase = path.resolve(mediaFolder);\n  \n  if (!resolved.startsWith(resolvedBase + path.sep) && resolved !== resolvedBase) {\n    throw new Error('Path traversal detected');\n  }\n  \n  return resolved;\n}\n```\n\n---\n\n## References\n\n- [CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')](https://cwe.mitre.org/data/definitions/22.html)\n- [OWASP Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal)\n- [Node.js path.join() Documentation](https://nodejs.org/api/path.html#pathjoinpaths)\n- [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)",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "tinacms"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "2.1.7"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/tinacms/tinacms/security/advisories/GHSA-5hxf-c7j4-279c"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-28791"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/tinacms/tinacms"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-22" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-03-12T18:32:38Z",
    "nvd_published_at" : "2026-03-12T17:16:50Z"
  }
}