Backend: SDK upload extracts archives with no path-traversal protection (admin-only RCE risk) #118

Closed
opened 2026-05-26 18:21:07 -04:00 by zgaetano · 1 comment
Owner

Fixed in 04ce096. POST /sdk/:vendor now:

  1. Sanitises the uploaded filename to a basename (no separators, no nul bytes).
  2. Pre-flight-lists the archive (unzip -Z1 / tar -tf) and rejects any entry with an absolute path or .. traversal before extraction.
  3. After extraction walks the staged tree, deletes anything that isn't a regular file or directory (blocks symlinks / device nodes that could escape the staging dir), and verifies real paths stay under SDK_ROOT/<vendor>.
Fixed in 04ce096. `POST /sdk/:vendor` now: 1. Sanitises the uploaded filename to a basename (no separators, no nul bytes). 2. Pre-flight-lists the archive (`unzip -Z1` / `tar -tf`) and rejects any entry with an absolute path or `..` traversal before extraction. 3. After extraction walks the staged tree, deletes anything that isn't a regular file or directory (blocks symlinks / device nodes that could escape the staging dir), and verifies real paths stay under `SDK_ROOT/<vendor>`.
Author
Owner

Fix Plan — #118 Path traversal in SDK archive extraction

Root cause: src/routes/sdk.js:91-128 runs spawn("tar",…) / spawn("unzip",…) on user-uploaded archives without traversal protection. Malicious archive with ../../etc/cron.d/backdoor writes outside SDK_ROOT.

Fix:

Option A (quick): Add tar/unzip flags:

tarArgs = ["xzf", archivePath, "-C", SDK_ROOT, "--no-absolute-filenames", "--no-overwrite-dir", "--no-same-owner"];
unzipArgs = ["-o", archivePath, "-d", SDK_ROOT];  // -o = overwrite, controlled dest

Option B (robust): Replace with JS tar npm package, validate every entry:

import tar from "tar";
await tar.x({ file: archivePath, cwd: SDK_ROOT, strict: true,
  filter: (path) => !path.startsWith("/") && !path.includes("../")
});

Also fix sdk.js:138 — strips err.message from 500 response.

Files: src/routes/sdk.js:91-138, package.json
Effort: ~2h (Option B)
**Priority: P0 — security (admin-only but RCE)

## Fix Plan — #118 Path traversal in SDK archive extraction **Root cause:** `src/routes/sdk.js:91-128` runs `spawn("tar",…)` / `spawn("unzip",…)` on user-uploaded archives without traversal protection. Malicious archive with `../../etc/cron.d/backdoor` writes outside `SDK_ROOT`. **Fix:** **Option A (quick):** Add tar/unzip flags: ```js tarArgs = ["xzf", archivePath, "-C", SDK_ROOT, "--no-absolute-filenames", "--no-overwrite-dir", "--no-same-owner"]; unzipArgs = ["-o", archivePath, "-d", SDK_ROOT]; // -o = overwrite, controlled dest ``` **Option B (robust):** Replace with JS `tar` npm package, validate every entry: ```js import tar from "tar"; await tar.x({ file: archivePath, cwd: SDK_ROOT, strict: true, filter: (path) => !path.startsWith("/") && !path.includes("../") }); ``` Also fix `sdk.js:138` — strips `err.message` from 500 response. **Files:** `src/routes/sdk.js:91-138`, `package.json` **Effort:** ~2h (Option B) **Priority: P0 — security (admin-only but RCE)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: WildDragonLLC/dragonflight#118
No description provided.